@hanzogui/sheet 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/controller/index.cjs +2 -0
- package/controller/index.d.ts +5 -0
- package/controller/index.js +2 -0
- package/controller/index.native.cjs +2 -0
- package/controller/index.native.js +2 -0
- package/dist/cjs/GestureDetectorWrapper.cjs +48 -0
- package/dist/cjs/GestureDetectorWrapper.native.js +53 -0
- package/dist/cjs/GestureDetectorWrapper.native.js.map +1 -0
- package/dist/cjs/GestureSheetContext.cjs +52 -0
- package/dist/cjs/GestureSheetContext.native.js +56 -0
- package/dist/cjs/GestureSheetContext.native.js.map +1 -0
- package/dist/cjs/Sheet.cjs +117 -0
- package/dist/cjs/Sheet.native.js +120 -0
- package/dist/cjs/Sheet.native.js.map +1 -0
- package/dist/cjs/SheetContext.cjs +32 -0
- package/dist/cjs/SheetContext.native.js +35 -0
- package/dist/cjs/SheetContext.native.js.map +1 -0
- package/dist/cjs/SheetController.cjs +65 -0
- package/dist/cjs/SheetController.native.js +71 -0
- package/dist/cjs/SheetController.native.js.map +1 -0
- package/dist/cjs/SheetImplementationCustom.cjs +472 -0
- package/dist/cjs/SheetImplementationCustom.native.js +524 -0
- package/dist/cjs/SheetImplementationCustom.native.js.map +1 -0
- package/dist/cjs/SheetScrollView.cjs +180 -0
- package/dist/cjs/SheetScrollView.native.js +197 -0
- package/dist/cjs/SheetScrollView.native.js.map +1 -0
- package/dist/cjs/constants.cjs +32 -0
- package/dist/cjs/constants.native.js +35 -0
- package/dist/cjs/constants.native.js.map +1 -0
- package/dist/cjs/contexts.cjs +42 -0
- package/dist/cjs/contexts.native.js +45 -0
- package/dist/cjs/contexts.native.js.map +1 -0
- package/dist/cjs/controller.cjs +29 -0
- package/dist/cjs/controller.native.js +32 -0
- package/dist/cjs/controller.native.js.map +1 -0
- package/dist/cjs/createSheet.cjs +191 -0
- package/dist/cjs/createSheet.native.js +208 -0
- package/dist/cjs/createSheet.native.js.map +1 -0
- package/dist/cjs/gestureState.cjs +39 -0
- package/dist/cjs/gestureState.native.js +42 -0
- package/dist/cjs/gestureState.native.js.map +1 -0
- package/dist/cjs/helpers.cjs +31 -0
- package/dist/cjs/helpers.native.js +35 -0
- package/dist/cjs/helpers.native.js.map +1 -0
- package/dist/cjs/index.cjs +28 -0
- package/dist/cjs/index.native.js +31 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/nativeSheet.cjs +87 -0
- package/dist/cjs/nativeSheet.native.js +101 -0
- package/dist/cjs/nativeSheet.native.js.map +1 -0
- package/dist/cjs/setupGestureHandler.cjs +46 -0
- package/dist/cjs/setupGestureHandler.native.js +50 -0
- package/dist/cjs/setupGestureHandler.native.js.map +1 -0
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.native.js +19 -0
- package/dist/cjs/types.native.js.map +1 -0
- package/dist/cjs/useGestureHandlerPan.cjs +136 -0
- package/dist/cjs/useGestureHandlerPan.native.js +145 -0
- package/dist/cjs/useGestureHandlerPan.native.js.map +1 -0
- package/dist/cjs/useKeyboardControllerSheet.cjs +38 -0
- package/dist/cjs/useKeyboardControllerSheet.native.js +114 -0
- package/dist/cjs/useKeyboardControllerSheet.native.js.map +1 -0
- package/dist/cjs/useSheet.cjs +27 -0
- package/dist/cjs/useSheet.native.js +32 -0
- package/dist/cjs/useSheet.native.js.map +1 -0
- package/dist/cjs/useSheetController.cjs +50 -0
- package/dist/cjs/useSheetController.native.js +53 -0
- package/dist/cjs/useSheetController.native.js.map +1 -0
- package/dist/cjs/useSheetOffscreenSize.cjs +51 -0
- package/dist/cjs/useSheetOffscreenSize.native.js +61 -0
- package/dist/cjs/useSheetOffscreenSize.native.js.map +1 -0
- package/dist/cjs/useSheetOpenState.cjs +49 -0
- package/dist/cjs/useSheetOpenState.native.js +56 -0
- package/dist/cjs/useSheetOpenState.native.js.map +1 -0
- package/dist/cjs/useSheetProviderProps.cjs +133 -0
- package/dist/cjs/useSheetProviderProps.native.js +153 -0
- package/dist/cjs/useSheetProviderProps.native.js.map +1 -0
- package/dist/cjs/useSheetScrollViewGestures.cjs +123 -0
- package/dist/cjs/useSheetScrollViewGestures.native.js +110 -0
- package/dist/cjs/useSheetScrollViewGestures.native.js.map +1 -0
- package/dist/esm/GestureDetectorWrapper.mjs +25 -0
- package/dist/esm/GestureDetectorWrapper.mjs.map +1 -0
- package/dist/esm/GestureDetectorWrapper.native.js +27 -0
- package/dist/esm/GestureDetectorWrapper.native.js.map +1 -0
- package/dist/esm/GestureSheetContext.mjs +28 -0
- package/dist/esm/GestureSheetContext.mjs.map +1 -0
- package/dist/esm/GestureSheetContext.native.js +29 -0
- package/dist/esm/GestureSheetContext.native.js.map +1 -0
- package/dist/esm/Sheet.mjs +89 -0
- package/dist/esm/Sheet.mjs.map +1 -0
- package/dist/esm/Sheet.native.js +89 -0
- package/dist/esm/Sheet.native.js.map +1 -0
- package/dist/esm/SheetContext.mjs +6 -0
- package/dist/esm/SheetContext.mjs.map +1 -0
- package/dist/esm/SheetContext.native.js +6 -0
- package/dist/esm/SheetContext.native.js.map +1 -0
- package/dist/esm/SheetController.mjs +31 -0
- package/dist/esm/SheetController.mjs.map +1 -0
- package/dist/esm/SheetController.native.js +34 -0
- package/dist/esm/SheetController.native.js.map +1 -0
- package/dist/esm/SheetImplementationCustom.mjs +437 -0
- package/dist/esm/SheetImplementationCustom.mjs.map +1 -0
- package/dist/esm/SheetImplementationCustom.native.js +487 -0
- package/dist/esm/SheetImplementationCustom.native.js.map +1 -0
- package/dist/esm/SheetScrollView.mjs +146 -0
- package/dist/esm/SheetScrollView.mjs.map +1 -0
- package/dist/esm/SheetScrollView.native.js +160 -0
- package/dist/esm/SheetScrollView.native.js.map +1 -0
- package/dist/esm/constants.mjs +6 -0
- package/dist/esm/constants.mjs.map +1 -0
- package/dist/esm/constants.native.js +6 -0
- package/dist/esm/constants.native.js.map +1 -0
- package/dist/esm/contexts.mjs +7 -0
- package/dist/esm/contexts.mjs.map +1 -0
- package/dist/esm/contexts.native.js +7 -0
- package/dist/esm/contexts.native.js.map +1 -0
- package/dist/esm/controller.mjs +4 -0
- package/dist/esm/controller.mjs.map +1 -0
- package/dist/esm/controller.native.js +4 -0
- package/dist/esm/controller.native.js.map +1 -0
- package/dist/esm/createSheet.mjs +168 -0
- package/dist/esm/createSheet.mjs.map +1 -0
- package/dist/esm/createSheet.native.js +182 -0
- package/dist/esm/createSheet.native.js.map +1 -0
- package/dist/esm/gestureState.mjs +13 -0
- package/dist/esm/gestureState.mjs.map +1 -0
- package/dist/esm/gestureState.native.js +13 -0
- package/dist/esm/gestureState.native.js.map +1 -0
- package/dist/esm/helpers.mjs +8 -0
- package/dist/esm/helpers.mjs.map +1 -0
- package/dist/esm/helpers.native.js +9 -0
- package/dist/esm/helpers.native.js.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/index.mjs +12 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +12 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/nativeSheet.mjs +63 -0
- package/dist/esm/nativeSheet.mjs.map +1 -0
- package/dist/esm/nativeSheet.native.js +74 -0
- package/dist/esm/nativeSheet.native.js.map +1 -0
- package/dist/esm/setupGestureHandler.mjs +22 -0
- package/dist/esm/setupGestureHandler.mjs.map +1 -0
- package/dist/esm/setupGestureHandler.native.js +23 -0
- package/dist/esm/setupGestureHandler.native.js.map +1 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/dist/esm/useGestureHandlerPan.mjs +113 -0
- package/dist/esm/useGestureHandlerPan.mjs.map +1 -0
- package/dist/esm/useGestureHandlerPan.native.js +119 -0
- package/dist/esm/useGestureHandlerPan.native.js.map +1 -0
- package/dist/esm/useKeyboardControllerSheet.mjs +15 -0
- package/dist/esm/useKeyboardControllerSheet.mjs.map +1 -0
- package/dist/esm/useKeyboardControllerSheet.native.js +88 -0
- package/dist/esm/useKeyboardControllerSheet.native.js.map +1 -0
- package/dist/esm/useSheet.mjs +4 -0
- package/dist/esm/useSheet.mjs.map +1 -0
- package/dist/esm/useSheet.native.js +6 -0
- package/dist/esm/useSheet.native.js.map +1 -0
- package/dist/esm/useSheetController.mjs +15 -0
- package/dist/esm/useSheetController.mjs.map +1 -0
- package/dist/esm/useSheetController.native.js +15 -0
- package/dist/esm/useSheetController.native.js.map +1 -0
- package/dist/esm/useSheetOffscreenSize.mjs +28 -0
- package/dist/esm/useSheetOffscreenSize.mjs.map +1 -0
- package/dist/esm/useSheetOffscreenSize.native.js +35 -0
- package/dist/esm/useSheetOffscreenSize.native.js.map +1 -0
- package/dist/esm/useSheetOpenState.mjs +26 -0
- package/dist/esm/useSheetOpenState.mjs.map +1 -0
- package/dist/esm/useSheetOpenState.native.js +30 -0
- package/dist/esm/useSheetOpenState.native.js.map +1 -0
- package/dist/esm/useSheetProviderProps.mjs +99 -0
- package/dist/esm/useSheetProviderProps.mjs.map +1 -0
- package/dist/esm/useSheetProviderProps.native.js +116 -0
- package/dist/esm/useSheetProviderProps.native.js.map +1 -0
- package/dist/esm/useSheetScrollViewGestures.mjs +100 -0
- package/dist/esm/useSheetScrollViewGestures.mjs.map +1 -0
- package/dist/esm/useSheetScrollViewGestures.native.js +84 -0
- package/dist/esm/useSheetScrollViewGestures.native.js.map +1 -0
- package/dist/jsx/GestureDetectorWrapper.mjs +25 -0
- package/dist/jsx/GestureDetectorWrapper.mjs.map +1 -0
- package/dist/jsx/GestureDetectorWrapper.native.js +53 -0
- package/dist/jsx/GestureDetectorWrapper.native.js.map +1 -0
- package/dist/jsx/GestureSheetContext.mjs +28 -0
- package/dist/jsx/GestureSheetContext.mjs.map +1 -0
- package/dist/jsx/GestureSheetContext.native.js +56 -0
- package/dist/jsx/GestureSheetContext.native.js.map +1 -0
- package/dist/jsx/Sheet.mjs +89 -0
- package/dist/jsx/Sheet.mjs.map +1 -0
- package/dist/jsx/Sheet.native.js +120 -0
- package/dist/jsx/Sheet.native.js.map +1 -0
- package/dist/jsx/SheetContext.mjs +6 -0
- package/dist/jsx/SheetContext.mjs.map +1 -0
- package/dist/jsx/SheetContext.native.js +35 -0
- package/dist/jsx/SheetContext.native.js.map +1 -0
- package/dist/jsx/SheetController.mjs +31 -0
- package/dist/jsx/SheetController.mjs.map +1 -0
- package/dist/jsx/SheetController.native.js +71 -0
- package/dist/jsx/SheetController.native.js.map +1 -0
- package/dist/jsx/SheetImplementationCustom.mjs +437 -0
- package/dist/jsx/SheetImplementationCustom.mjs.map +1 -0
- package/dist/jsx/SheetImplementationCustom.native.js +524 -0
- package/dist/jsx/SheetImplementationCustom.native.js.map +1 -0
- package/dist/jsx/SheetScrollView.mjs +146 -0
- package/dist/jsx/SheetScrollView.mjs.map +1 -0
- package/dist/jsx/SheetScrollView.native.js +197 -0
- package/dist/jsx/SheetScrollView.native.js.map +1 -0
- package/dist/jsx/constants.mjs +6 -0
- package/dist/jsx/constants.mjs.map +1 -0
- package/dist/jsx/constants.native.js +35 -0
- package/dist/jsx/constants.native.js.map +1 -0
- package/dist/jsx/contexts.mjs +7 -0
- package/dist/jsx/contexts.mjs.map +1 -0
- package/dist/jsx/contexts.native.js +45 -0
- package/dist/jsx/contexts.native.js.map +1 -0
- package/dist/jsx/controller.mjs +4 -0
- package/dist/jsx/controller.mjs.map +1 -0
- package/dist/jsx/controller.native.js +32 -0
- package/dist/jsx/controller.native.js.map +1 -0
- package/dist/jsx/createSheet.mjs +168 -0
- package/dist/jsx/createSheet.mjs.map +1 -0
- package/dist/jsx/createSheet.native.js +208 -0
- package/dist/jsx/createSheet.native.js.map +1 -0
- package/dist/jsx/gestureState.mjs +13 -0
- package/dist/jsx/gestureState.mjs.map +1 -0
- package/dist/jsx/gestureState.native.js +42 -0
- package/dist/jsx/gestureState.native.js.map +1 -0
- package/dist/jsx/helpers.mjs +8 -0
- package/dist/jsx/helpers.mjs.map +1 -0
- package/dist/jsx/helpers.native.js +35 -0
- package/dist/jsx/helpers.native.js.map +1 -0
- package/dist/jsx/index.js +12 -0
- package/dist/jsx/index.js.map +1 -0
- package/dist/jsx/index.mjs +12 -0
- package/dist/jsx/index.mjs.map +1 -0
- package/dist/jsx/index.native.js +31 -0
- package/dist/jsx/index.native.js.map +1 -0
- package/dist/jsx/nativeSheet.mjs +63 -0
- package/dist/jsx/nativeSheet.mjs.map +1 -0
- package/dist/jsx/nativeSheet.native.js +101 -0
- package/dist/jsx/nativeSheet.native.js.map +1 -0
- package/dist/jsx/setupGestureHandler.mjs +22 -0
- package/dist/jsx/setupGestureHandler.mjs.map +1 -0
- package/dist/jsx/setupGestureHandler.native.js +50 -0
- package/dist/jsx/setupGestureHandler.native.js.map +1 -0
- package/dist/jsx/types.mjs +2 -0
- package/dist/jsx/types.mjs.map +1 -0
- package/dist/jsx/types.native.js +19 -0
- package/dist/jsx/types.native.js.map +1 -0
- package/dist/jsx/useGestureHandlerPan.mjs +113 -0
- package/dist/jsx/useGestureHandlerPan.mjs.map +1 -0
- package/dist/jsx/useGestureHandlerPan.native.js +145 -0
- package/dist/jsx/useGestureHandlerPan.native.js.map +1 -0
- package/dist/jsx/useKeyboardControllerSheet.mjs +15 -0
- package/dist/jsx/useKeyboardControllerSheet.mjs.map +1 -0
- package/dist/jsx/useKeyboardControllerSheet.native.js +114 -0
- package/dist/jsx/useKeyboardControllerSheet.native.js.map +1 -0
- package/dist/jsx/useSheet.mjs +4 -0
- package/dist/jsx/useSheet.mjs.map +1 -0
- package/dist/jsx/useSheet.native.js +32 -0
- package/dist/jsx/useSheet.native.js.map +1 -0
- package/dist/jsx/useSheetController.mjs +15 -0
- package/dist/jsx/useSheetController.mjs.map +1 -0
- package/dist/jsx/useSheetController.native.js +53 -0
- package/dist/jsx/useSheetController.native.js.map +1 -0
- package/dist/jsx/useSheetOffscreenSize.mjs +28 -0
- package/dist/jsx/useSheetOffscreenSize.mjs.map +1 -0
- package/dist/jsx/useSheetOffscreenSize.native.js +61 -0
- package/dist/jsx/useSheetOffscreenSize.native.js.map +1 -0
- package/dist/jsx/useSheetOpenState.mjs +26 -0
- package/dist/jsx/useSheetOpenState.mjs.map +1 -0
- package/dist/jsx/useSheetOpenState.native.js +56 -0
- package/dist/jsx/useSheetOpenState.native.js.map +1 -0
- package/dist/jsx/useSheetProviderProps.mjs +99 -0
- package/dist/jsx/useSheetProviderProps.mjs.map +1 -0
- package/dist/jsx/useSheetProviderProps.native.js +153 -0
- package/dist/jsx/useSheetProviderProps.native.js.map +1 -0
- package/dist/jsx/useSheetScrollViewGestures.mjs +100 -0
- package/dist/jsx/useSheetScrollViewGestures.mjs.map +1 -0
- package/dist/jsx/useSheetScrollViewGestures.native.js +110 -0
- package/dist/jsx/useSheetScrollViewGestures.native.js.map +1 -0
- package/next.md +78 -0
- package/package.json +96 -0
- package/setup-gesture-handler/index.cjs +2 -0
- package/setup-gesture-handler/index.js +2 -0
- package/setup-gesture-handler/index.native.cjs +2 -0
- package/setup-gesture-handler/index.native.js +2 -0
- package/setup-gesture-handler.cjs +1 -0
- package/src/GestureDetectorWrapper.tsx +41 -0
- package/src/GestureSheetContext.tsx +62 -0
- package/src/Sheet.tsx +114 -0
- package/src/SheetContext.tsx +11 -0
- package/src/SheetController.tsx +44 -0
- package/src/SheetImplementationCustom.tsx +868 -0
- package/src/SheetScrollView.tsx +195 -0
- package/src/constants.tsx +5 -0
- package/src/contexts.ts +9 -0
- package/src/controller.ts +9 -0
- package/src/createSheet.tsx +296 -0
- package/src/gestureState.ts +24 -0
- package/src/helpers.tsx +31 -0
- package/src/index.ts +11 -0
- package/src/nativeSheet.tsx +89 -0
- package/src/setupGestureHandler.ts +34 -0
- package/src/types.tsx +181 -0
- package/src/useGestureHandlerPan.tsx +364 -0
- package/src/useKeyboardControllerSheet.native.ts +136 -0
- package/src/useKeyboardControllerSheet.ts +26 -0
- package/src/useSheet.tsx +3 -0
- package/src/useSheetController.tsx +27 -0
- package/src/useSheetOffscreenSize.tsx +49 -0
- package/src/useSheetOpenState.tsx +33 -0
- package/src/useSheetProviderProps.tsx +223 -0
- package/src/useSheetScrollViewGestures.native.ts +163 -0
- package/src/useSheetScrollViewGestures.ts +194 -0
- package/tsconfig.json +56 -0
- package/types/GestureDetectorWrapper.d.ts +14 -0
- package/types/GestureSheetContext.d.ts +36 -0
- package/types/Sheet.d.ts +183 -0
- package/types/SheetContext.d.ts +43 -0
- package/types/SheetController.d.ts +6 -0
- package/types/SheetImplementationCustom.d.ts +36 -0
- package/types/SheetScrollView.d.ts +40 -0
- package/types/constants.d.ts +5 -0
- package/types/contexts.d.ts +6 -0
- package/types/controller.d.ts +3 -0
- package/types/createSheet.d.ts +194 -0
- package/types/gestureState.d.ts +11 -0
- package/types/helpers.d.ts +2 -0
- package/types/index.d.ts +12 -0
- package/types/nativeSheet.d.ts +10 -0
- package/types/setupGestureHandler.d.ts +11 -0
- package/types/types.d.ts +147 -0
- package/types/useGestureHandlerPan.d.ts +44 -0
- package/types/useKeyboardControllerSheet.d.ts +7 -0
- package/types/useKeyboardControllerSheet.native.d.ts +13 -0
- package/types/useSheet.d.ts +2 -0
- package/types/useSheetController.d.ts +17 -0
- package/types/useSheetOffscreenSize.d.ts +3 -0
- package/types/useSheetOpenState.d.ts +9 -0
- package/types/useSheetProviderProps.d.ts +36 -0
- package/types/useSheetScrollViewGestures.d.ts +12 -0
- package/types/useSheetScrollViewGestures.native.d.ts +21 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native implementation of keyboard controller sheet hook.
|
|
3
|
+
*
|
|
4
|
+
* Simplified to just track keyboard state (height, visibility).
|
|
5
|
+
* Position animation is handled by SheetImplementationCustom via
|
|
6
|
+
* keyboard-adjusted positions — matching the react-native-actions-sheet pattern.
|
|
7
|
+
*
|
|
8
|
+
* Uses react-native-keyboard-controller events when available,
|
|
9
|
+
* falls back to basic Keyboard API otherwise.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
13
|
+
import { Keyboard, Platform } from 'react-native'
|
|
14
|
+
import type {
|
|
15
|
+
KeyboardControllerSheetOptions,
|
|
16
|
+
KeyboardControllerSheetResult,
|
|
17
|
+
} from './types'
|
|
18
|
+
|
|
19
|
+
// lazy import state accessors
|
|
20
|
+
let isKeyboardControllerEnabled: () => boolean = () => false
|
|
21
|
+
let getKeyboardControllerState: () => any = () => ({})
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const nativeModule = require('@hanzogui/native')
|
|
25
|
+
isKeyboardControllerEnabled = nativeModule.isKeyboardControllerEnabled
|
|
26
|
+
getKeyboardControllerState = nativeModule.getKeyboardControllerState
|
|
27
|
+
} catch {
|
|
28
|
+
// @hanzogui/native not available
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useKeyboardControllerSheet(
|
|
32
|
+
options: KeyboardControllerSheetOptions
|
|
33
|
+
): KeyboardControllerSheetResult {
|
|
34
|
+
const { enabled } = options
|
|
35
|
+
|
|
36
|
+
const [keyboardHeight, setKeyboardHeight] = useState(0)
|
|
37
|
+
const [isKeyboardVisible, setIsKeyboardVisible] = useState(false)
|
|
38
|
+
const keyboardControllerEnabled = isKeyboardControllerEnabled()
|
|
39
|
+
|
|
40
|
+
// action-sheet pattern: pause keyboard hide events during drag
|
|
41
|
+
// when true, keyboard hide events are ignored so isKeyboardVisible stays true
|
|
42
|
+
// and activePositions don't revert mid-gesture
|
|
43
|
+
const pauseKeyboardHandler = useRef(false)
|
|
44
|
+
// tracks if a keyboard hide event was suppressed while paused
|
|
45
|
+
const pendingHide = useRef(false)
|
|
46
|
+
|
|
47
|
+
// dismiss keyboard helper
|
|
48
|
+
const dismissKeyboard = useCallback(() => {
|
|
49
|
+
Keyboard.dismiss()
|
|
50
|
+
if (keyboardControllerEnabled) {
|
|
51
|
+
try {
|
|
52
|
+
const { KeyboardController } = getKeyboardControllerState()
|
|
53
|
+
KeyboardController?.dismiss?.()
|
|
54
|
+
} catch {
|
|
55
|
+
// ignore errors from keyboard-controller
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}, [keyboardControllerEnabled])
|
|
59
|
+
|
|
60
|
+
// flush any keyboard hide that was suppressed during drag.
|
|
61
|
+
// called when drag ends to reconcile actual keyboard state.
|
|
62
|
+
const flushPendingHide = useCallback(() => {
|
|
63
|
+
if (pendingHide.current) {
|
|
64
|
+
pendingHide.current = false
|
|
65
|
+
setIsKeyboardVisible(false)
|
|
66
|
+
setKeyboardHeight(0)
|
|
67
|
+
}
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
// keyboard-controller event listeners (preferred when available)
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!enabled || !keyboardControllerEnabled) return
|
|
73
|
+
|
|
74
|
+
const { KeyboardEvents } = getKeyboardControllerState()
|
|
75
|
+
if (!KeyboardEvents?.addListener) return
|
|
76
|
+
|
|
77
|
+
const showSub = KeyboardEvents.addListener('keyboardWillShow', (e: any) => {
|
|
78
|
+
const height = e?.height ?? 0
|
|
79
|
+
if (height > 0) {
|
|
80
|
+
setKeyboardHeight(height)
|
|
81
|
+
}
|
|
82
|
+
setIsKeyboardVisible(true)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const hideSub = KeyboardEvents.addListener('keyboardWillHide', () => {
|
|
86
|
+
if (pauseKeyboardHandler.current) {
|
|
87
|
+
pendingHide.current = true
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
setIsKeyboardVisible(false)
|
|
91
|
+
setKeyboardHeight(0)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
showSub?.remove?.()
|
|
96
|
+
hideSub?.remove?.()
|
|
97
|
+
}
|
|
98
|
+
}, [enabled, keyboardControllerEnabled])
|
|
99
|
+
|
|
100
|
+
// fallback to basic Keyboard API when keyboard-controller not available
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!enabled) return
|
|
103
|
+
if (keyboardControllerEnabled) return
|
|
104
|
+
|
|
105
|
+
const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'
|
|
106
|
+
const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'
|
|
107
|
+
|
|
108
|
+
const showListener = Keyboard.addListener(showEvent, (e) => {
|
|
109
|
+
setKeyboardHeight(e.endCoordinates.height)
|
|
110
|
+
setIsKeyboardVisible(true)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const hideListener = Keyboard.addListener(hideEvent, () => {
|
|
114
|
+
if (pauseKeyboardHandler.current) {
|
|
115
|
+
pendingHide.current = true
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
setIsKeyboardVisible(false)
|
|
119
|
+
setKeyboardHeight(0)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
return () => {
|
|
123
|
+
showListener.remove()
|
|
124
|
+
hideListener.remove()
|
|
125
|
+
}
|
|
126
|
+
}, [enabled, keyboardControllerEnabled])
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
keyboardControllerEnabled,
|
|
130
|
+
keyboardHeight,
|
|
131
|
+
isKeyboardVisible,
|
|
132
|
+
dismissKeyboard,
|
|
133
|
+
pauseKeyboardHandler,
|
|
134
|
+
flushPendingHide,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web stub for keyboard controller sheet hook.
|
|
3
|
+
* Returns no-op values since keyboard-controller is native-only.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useRef } from 'react'
|
|
7
|
+
import type {
|
|
8
|
+
KeyboardControllerSheetOptions,
|
|
9
|
+
KeyboardControllerSheetResult,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
const noop = () => {}
|
|
13
|
+
|
|
14
|
+
export function useKeyboardControllerSheet(
|
|
15
|
+
_options: KeyboardControllerSheetOptions
|
|
16
|
+
): KeyboardControllerSheetResult {
|
|
17
|
+
const pauseKeyboardHandler = useRef(false)
|
|
18
|
+
return {
|
|
19
|
+
keyboardControllerEnabled: false,
|
|
20
|
+
keyboardHeight: 0,
|
|
21
|
+
isKeyboardVisible: false,
|
|
22
|
+
dismissKeyboard: noop,
|
|
23
|
+
pauseKeyboardHandler,
|
|
24
|
+
flushPendingHide: noop,
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/useSheet.tsx
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
export const useSheetController = () => {
|
|
4
|
+
const controller = React.useContext(SheetControllerContext)
|
|
5
|
+
const isHidden = controller?.hidden
|
|
6
|
+
const isShowingNonSheet = isHidden && controller?.open
|
|
7
|
+
return {
|
|
8
|
+
controller,
|
|
9
|
+
isHidden,
|
|
10
|
+
isShowingNonSheet,
|
|
11
|
+
disableDrag: controller?.disableDrag,
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const SheetControllerContext =
|
|
16
|
+
React.createContext<SheetControllerContextValue | null>(null)
|
|
17
|
+
|
|
18
|
+
export type SheetControllerContextValue = {
|
|
19
|
+
id?: string
|
|
20
|
+
disableDrag?: boolean
|
|
21
|
+
open?: boolean
|
|
22
|
+
// hide without "closing" to prevent re-animation when shown again
|
|
23
|
+
hidden?: boolean
|
|
24
|
+
onOpenChange?: React.Dispatch<React.SetStateAction<boolean>> | ((val: boolean) => void)
|
|
25
|
+
// when true, the sheet should skip its open animation (used for adapt handoff)
|
|
26
|
+
skipNextAnimation?: boolean
|
|
27
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { SheetContextValue } from './useSheetProviderProps'
|
|
2
|
+
|
|
3
|
+
export const useSheetOffscreenSize = ({
|
|
4
|
+
snapPoints,
|
|
5
|
+
position,
|
|
6
|
+
screenSize,
|
|
7
|
+
frameSize,
|
|
8
|
+
snapPointsMode,
|
|
9
|
+
}: SheetContextValue) => {
|
|
10
|
+
if (snapPointsMode === 'fit') {
|
|
11
|
+
return 0
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (snapPointsMode === 'constant') {
|
|
15
|
+
const maxSize = Number(snapPoints[0])
|
|
16
|
+
const currentSize = Number(snapPoints[position] ?? 0)
|
|
17
|
+
const offscreenSize = maxSize - currentSize
|
|
18
|
+
return offscreenSize
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (snapPointsMode === 'percent') {
|
|
22
|
+
const maxPercentOpened = Number(snapPoints[0]) / 100
|
|
23
|
+
const percentOpened = Number(snapPoints[position] ?? 0) / 100
|
|
24
|
+
const percentHidden = maxPercentOpened - percentOpened
|
|
25
|
+
const offscreenSize = percentHidden * screenSize
|
|
26
|
+
return offscreenSize
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// mixed:
|
|
30
|
+
const maxSnapPoint = snapPoints[0]
|
|
31
|
+
if (maxSnapPoint === 'fit') {
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const maxSize =
|
|
36
|
+
typeof maxSnapPoint === 'string'
|
|
37
|
+
? (Number(maxSnapPoint.slice(0, -1)) / 100) * screenSize
|
|
38
|
+
: maxSnapPoint
|
|
39
|
+
const currentSnapPoint = snapPoints[position] ?? 0
|
|
40
|
+
const currentSize =
|
|
41
|
+
typeof currentSnapPoint === 'string'
|
|
42
|
+
? (Number(currentSnapPoint.slice(0, -1)) / 100) * screenSize
|
|
43
|
+
: currentSnapPoint
|
|
44
|
+
const offscreenSize = maxSize - currentSize
|
|
45
|
+
if (Number.isNaN(offscreenSize)) {
|
|
46
|
+
return 0
|
|
47
|
+
}
|
|
48
|
+
return offscreenSize
|
|
49
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useControllableState } from '@hanzogui/use-controllable-state'
|
|
2
|
+
|
|
3
|
+
import type { SheetProps } from './types'
|
|
4
|
+
import { useSheetController } from './useSheetController'
|
|
5
|
+
|
|
6
|
+
export const useSheetOpenState = (props: SheetProps) => {
|
|
7
|
+
const { isHidden, controller } = useSheetController()
|
|
8
|
+
|
|
9
|
+
const onOpenChangeInternal = (val: boolean) => {
|
|
10
|
+
controller?.onOpenChange?.(val)
|
|
11
|
+
props.onOpenChange?.(val)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const propVal = props.preferAdaptParentOpenState
|
|
15
|
+
? (controller?.open ?? props.open)
|
|
16
|
+
: (props.open ?? controller?.open)
|
|
17
|
+
|
|
18
|
+
const [open, setOpen] = useControllableState({
|
|
19
|
+
prop: propVal,
|
|
20
|
+
defaultProp: props.defaultOpen ?? false,
|
|
21
|
+
onChange: onOpenChangeInternal,
|
|
22
|
+
strategy: 'most-recent-wins',
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
open,
|
|
27
|
+
setOpen,
|
|
28
|
+
isHidden,
|
|
29
|
+
controller,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type SheetOpenState = ReturnType<typeof useSheetOpenState>
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { GuiElement } from '@hanzogui/core'
|
|
3
|
+
import { useConfiguration } from '@hanzogui/core'
|
|
4
|
+
import { useConstant } from '@hanzogui/use-constant'
|
|
5
|
+
import { useControllableState } from '@hanzogui/use-controllable-state'
|
|
6
|
+
|
|
7
|
+
import type { ScrollBridge, SheetProps } from './types'
|
|
8
|
+
import type { SheetOpenState } from './useSheetOpenState'
|
|
9
|
+
|
|
10
|
+
export type SheetContextValue = ReturnType<typeof useSheetProviderProps> & {
|
|
11
|
+
setHasScrollView: (val: boolean) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useSheetProviderProps(
|
|
15
|
+
props: SheetProps,
|
|
16
|
+
state: SheetOpenState,
|
|
17
|
+
options: {
|
|
18
|
+
onOverlayComponent?: (comp: any) => void
|
|
19
|
+
} = {}
|
|
20
|
+
) {
|
|
21
|
+
const handleRef = React.useRef<GuiElement>(null)
|
|
22
|
+
const contentRef = React.useRef<GuiElement>(null)
|
|
23
|
+
const [frameSize, setFrameSize] = React.useState<number>(0)
|
|
24
|
+
const [maxContentSize, setMaxContentSize] = React.useState<number>(0)
|
|
25
|
+
const snapPointsMode = props.snapPointsMode ?? 'percent'
|
|
26
|
+
const snapPointsProp: (string | number)[] =
|
|
27
|
+
props.snapPoints ??
|
|
28
|
+
(snapPointsMode === 'percent'
|
|
29
|
+
? [80]
|
|
30
|
+
: snapPointsMode === 'constant'
|
|
31
|
+
? [256]
|
|
32
|
+
: ['fit'])
|
|
33
|
+
const hasFit = snapPointsProp[0] === 'fit'
|
|
34
|
+
|
|
35
|
+
const snapPoints = React.useMemo(
|
|
36
|
+
() => (props.dismissOnSnapToBottom ? [...snapPointsProp, 0] : snapPointsProp),
|
|
37
|
+
|
|
38
|
+
[JSON.stringify(snapPointsProp), props.dismissOnSnapToBottom]
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
// lets set -1 to be always the "open = false" position
|
|
42
|
+
const [position_, setPositionImmediate] = useControllableState({
|
|
43
|
+
prop: props.position,
|
|
44
|
+
defaultProp: props.defaultPosition || (state.open ? 0 : -1),
|
|
45
|
+
onChange: props.onPositionChange,
|
|
46
|
+
strategy: 'most-recent-wins',
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const position = state.open === false ? -1 : position_
|
|
50
|
+
|
|
51
|
+
const { open } = state
|
|
52
|
+
|
|
53
|
+
const setPosition = React.useCallback(
|
|
54
|
+
(next: number) => {
|
|
55
|
+
// close on dismissOnSnapToBottom (and set position so it animates)
|
|
56
|
+
if (props.dismissOnSnapToBottom && next === snapPoints.length - 1) {
|
|
57
|
+
state.setOpen(false)
|
|
58
|
+
} else {
|
|
59
|
+
setPositionImmediate(next)
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[props.dismissOnSnapToBottom, snapPoints.length, setPositionImmediate, state.setOpen]
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if (process.env.NODE_ENV === 'development') {
|
|
66
|
+
if (
|
|
67
|
+
snapPointsMode === 'mixed' &&
|
|
68
|
+
snapPoints.some((p) => {
|
|
69
|
+
if (typeof p === 'string') {
|
|
70
|
+
if (p === 'fit') {
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
if (p.endsWith('%')) {
|
|
74
|
+
const n = Number(p.slice(0, -1))
|
|
75
|
+
return n < 0 || n > 100
|
|
76
|
+
}
|
|
77
|
+
return true
|
|
78
|
+
}
|
|
79
|
+
return typeof p !== 'number' || p < 0
|
|
80
|
+
})
|
|
81
|
+
) {
|
|
82
|
+
console.warn(
|
|
83
|
+
'⚠️ Invalid snapPoint given, snapPoints must be positive numeric values, string percentages between 0-100%, or "fit" when snapPointsMode is mixed'
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
if (snapPointsMode === 'mixed' && snapPoints.indexOf('fit') > 0) {
|
|
87
|
+
console.warn(
|
|
88
|
+
'⚠️ Invalid snapPoint given, "fit" must be the first/largest snap point when snapPointsMode is mixed'
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
if (
|
|
92
|
+
snapPointsMode === 'fit' &&
|
|
93
|
+
(snapPoints.length !== (props.dismissOnSnapToBottom ? 2 : 1) ||
|
|
94
|
+
snapPoints[0] !== 'fit')
|
|
95
|
+
) {
|
|
96
|
+
console.warn(
|
|
97
|
+
'⚠️ Invalid snapPoint given, there are no snap points when snapPointsMode is fit'
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
if (
|
|
101
|
+
snapPointsMode === 'constant' &&
|
|
102
|
+
snapPoints.some((p) => typeof p !== 'number' || p < 0)
|
|
103
|
+
) {
|
|
104
|
+
console.warn(
|
|
105
|
+
'⚠️ Invalid snapPoint given, snapPoints must be positive numeric values when snapPointsMode is constant'
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
if (
|
|
109
|
+
snapPointsMode === 'percent' &&
|
|
110
|
+
snapPoints.some((p) => typeof p !== 'number' || p < 0 || p > 100)
|
|
111
|
+
) {
|
|
112
|
+
console.warn(
|
|
113
|
+
'⚠️ Invalid snapPoint given, snapPoints must be numeric values between 0 and 100 when snapPointsMode is percent'
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// reset position to fully open on re-open after dismissOnSnapToBottom
|
|
119
|
+
if (open && props.dismissOnSnapToBottom && position === snapPoints.length - 1) {
|
|
120
|
+
setPositionImmediate(0)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// open must set position
|
|
124
|
+
const shouldSetPositionOpen = open && position < 0
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
if (shouldSetPositionOpen) {
|
|
127
|
+
setPosition(0)
|
|
128
|
+
}
|
|
129
|
+
}, [setPosition, shouldSetPositionOpen])
|
|
130
|
+
|
|
131
|
+
const { animationDriver } = useConfiguration()
|
|
132
|
+
if (!animationDriver) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
process.env.NODE_ENV === 'production'
|
|
135
|
+
? `❌ 008`
|
|
136
|
+
: 'Must set animations in gui.config.ts'
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const scrollBridge = useConstant<ScrollBridge>(() => {
|
|
141
|
+
const parentDragListeners = new Set<Function>()
|
|
142
|
+
|
|
143
|
+
const bridge: ScrollBridge = {
|
|
144
|
+
hasScrollableContent: false,
|
|
145
|
+
enabled: false,
|
|
146
|
+
y: 0,
|
|
147
|
+
paneY: 0,
|
|
148
|
+
paneMinY: 0,
|
|
149
|
+
scrollStartY: -1,
|
|
150
|
+
drag: () => {},
|
|
151
|
+
release: () => {},
|
|
152
|
+
scrollLock: false,
|
|
153
|
+
isParentDragging: false,
|
|
154
|
+
onParentDragging: (cb) => {
|
|
155
|
+
parentDragListeners.add(cb)
|
|
156
|
+
return () => {
|
|
157
|
+
parentDragListeners.delete(cb)
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
setParentDragging: (val) => {
|
|
161
|
+
if (val !== bridge.isParentDragging) {
|
|
162
|
+
bridge.isParentDragging = val
|
|
163
|
+
parentDragListeners.forEach((cb) => cb(val))
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return bridge
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// Handle both new disableRemoveScroll and deprecated forceRemoveScrollEnabled
|
|
172
|
+
let disableRemoveScroll: boolean | undefined
|
|
173
|
+
if (props.disableRemoveScroll !== undefined) {
|
|
174
|
+
// New prop: disableRemoveScroll={true} means RemoveScroll is disabled
|
|
175
|
+
disableRemoveScroll = props.disableRemoveScroll || !open || !props.modal
|
|
176
|
+
} else if (props.forceRemoveScrollEnabled !== undefined) {
|
|
177
|
+
// Deprecated prop: forceRemoveScrollEnabled can override the default
|
|
178
|
+
if (process.env.NODE_ENV === 'development') {
|
|
179
|
+
console.warn(
|
|
180
|
+
'[Sheet] forceRemoveScrollEnabled is deprecated. Use disableRemoveScroll instead.'
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
disableRemoveScroll = !props.forceRemoveScrollEnabled
|
|
184
|
+
} else {
|
|
185
|
+
// Default: disabled when not open or not modal
|
|
186
|
+
disableRemoveScroll = !open || !props.modal
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const maxSnapPoint = snapPoints[0]
|
|
190
|
+
const screenSize =
|
|
191
|
+
snapPointsMode === 'percent'
|
|
192
|
+
? frameSize / ((typeof maxSnapPoint === 'number' ? maxSnapPoint : 100) / 100)
|
|
193
|
+
: maxContentSize
|
|
194
|
+
|
|
195
|
+
const providerProps = {
|
|
196
|
+
screenSize,
|
|
197
|
+
maxSnapPoint,
|
|
198
|
+
disableRemoveScroll,
|
|
199
|
+
scrollBridge,
|
|
200
|
+
modal: !!props.modal,
|
|
201
|
+
open: state.open,
|
|
202
|
+
setOpen: state.setOpen,
|
|
203
|
+
hidden: !!state.isHidden,
|
|
204
|
+
contentRef,
|
|
205
|
+
handleRef,
|
|
206
|
+
frameSize,
|
|
207
|
+
setFrameSize,
|
|
208
|
+
dismissOnOverlayPress: props.dismissOnOverlayPress ?? true,
|
|
209
|
+
dismissOnSnapToBottom: props.dismissOnSnapToBottom ?? false,
|
|
210
|
+
onOverlayComponent: options.onOverlayComponent,
|
|
211
|
+
scope: props.__scopeSheet,
|
|
212
|
+
hasFit,
|
|
213
|
+
position,
|
|
214
|
+
snapPoints,
|
|
215
|
+
snapPointsMode,
|
|
216
|
+
setMaxContentSize,
|
|
217
|
+
setPosition,
|
|
218
|
+
setPositionImmediate,
|
|
219
|
+
onlyShowFrame: false,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return providerProps
|
|
223
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
2
|
+
import type { GestureResponderEvent, ScrollView as RNScrollView } from 'react-native'
|
|
3
|
+
import type { ScrollBridge } from './types'
|
|
4
|
+
|
|
5
|
+
interface ResponderState {
|
|
6
|
+
lastPageY: number
|
|
7
|
+
dragAt: number
|
|
8
|
+
dys: number[]
|
|
9
|
+
isScrolling: boolean
|
|
10
|
+
isDraggingScrollArea: boolean
|
|
11
|
+
scrollEngaged: boolean
|
|
12
|
+
prevScrollY: number
|
|
13
|
+
handoffOccurred: boolean
|
|
14
|
+
handoffDragOffset: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface UseSheetScrollViewGesturesProps {
|
|
18
|
+
scrollRef: React.RefObject<RNScrollView | null>
|
|
19
|
+
scrollBridge: ScrollBridge
|
|
20
|
+
hasScrollableContent: boolean
|
|
21
|
+
scrollEnabled: boolean
|
|
22
|
+
setScrollEnabled: (enabled: boolean, lockTo?: number) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Native gesture handling for Sheet ScrollView.
|
|
27
|
+
* Returns responder props for the ScrollView component.
|
|
28
|
+
*/
|
|
29
|
+
export function useSheetScrollViewGestures({
|
|
30
|
+
scrollBridge,
|
|
31
|
+
hasScrollableContent,
|
|
32
|
+
scrollEnabled,
|
|
33
|
+
setScrollEnabled,
|
|
34
|
+
}: UseSheetScrollViewGesturesProps) {
|
|
35
|
+
const state = useRef<ResponderState>({
|
|
36
|
+
lastPageY: 0,
|
|
37
|
+
dragAt: 0,
|
|
38
|
+
dys: [],
|
|
39
|
+
isScrolling: false,
|
|
40
|
+
isDraggingScrollArea: false,
|
|
41
|
+
scrollEngaged: false,
|
|
42
|
+
prevScrollY: 0,
|
|
43
|
+
handoffOccurred: false,
|
|
44
|
+
handoffDragOffset: 0,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const release = () => {
|
|
48
|
+
const s = state.current
|
|
49
|
+
if (!s.isDraggingScrollArea) return
|
|
50
|
+
|
|
51
|
+
s.isDraggingScrollArea = false
|
|
52
|
+
scrollBridge.scrollStartY = -1
|
|
53
|
+
scrollBridge.scrollLock = false
|
|
54
|
+
s.isScrolling = false
|
|
55
|
+
s.scrollEngaged = false
|
|
56
|
+
s.prevScrollY = 0
|
|
57
|
+
s.handoffOccurred = false
|
|
58
|
+
s.handoffDragOffset = 0
|
|
59
|
+
setScrollEnabled(true)
|
|
60
|
+
|
|
61
|
+
let vy = 0
|
|
62
|
+
if (s.dys.length) {
|
|
63
|
+
const recentDys = s.dys.slice(-10)
|
|
64
|
+
const dist = recentDys.reduce((a, b) => a + b, 0)
|
|
65
|
+
vy = (dist / recentDys.length) * 0.04
|
|
66
|
+
}
|
|
67
|
+
s.dys = []
|
|
68
|
+
|
|
69
|
+
scrollBridge.release({ dragAt: s.dragAt, vy })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const onStartShouldSetResponder = () => {
|
|
73
|
+
const s = state.current
|
|
74
|
+
scrollBridge.scrollStartY = -1
|
|
75
|
+
s.isDraggingScrollArea = true
|
|
76
|
+
s.scrollEngaged = scrollBridge.y > 0
|
|
77
|
+
s.prevScrollY = scrollBridge.y
|
|
78
|
+
s.handoffOccurred = false
|
|
79
|
+
s.handoffDragOffset = 0
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const onMoveShouldSetResponder = (e: GestureResponderEvent) => {
|
|
84
|
+
if (!scrollEnabled) return false
|
|
85
|
+
const s = state.current
|
|
86
|
+
const { pageY } = e.nativeEvent
|
|
87
|
+
if (s.lastPageY === 0) {
|
|
88
|
+
s.lastPageY = pageY
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
return Math.abs(pageY - s.lastPageY) > 10
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const onResponderMove = (e: GestureResponderEvent) => {
|
|
95
|
+
const s = state.current
|
|
96
|
+
const { pageY } = e.nativeEvent
|
|
97
|
+
|
|
98
|
+
if (!s.isScrolling && scrollBridge.scrollStartY === -1) {
|
|
99
|
+
scrollBridge.scrollStartY = pageY
|
|
100
|
+
s.lastPageY = pageY
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const dragAt = pageY - scrollBridge.scrollStartY
|
|
104
|
+
const dy = pageY - s.lastPageY
|
|
105
|
+
s.lastPageY = pageY
|
|
106
|
+
const isDraggingDown = dy > 0
|
|
107
|
+
const isPaneAtTop = scrollBridge.paneY <= scrollBridge.paneMinY + 5
|
|
108
|
+
const currentScrollY = scrollBridge.y
|
|
109
|
+
|
|
110
|
+
if (currentScrollY > 0) s.scrollEngaged = true
|
|
111
|
+
|
|
112
|
+
const wasScrolledNowAtZero =
|
|
113
|
+
s.scrollEngaged && s.prevScrollY > 0 && currentScrollY <= 0
|
|
114
|
+
|
|
115
|
+
if (wasScrolledNowAtZero && !s.handoffOccurred) {
|
|
116
|
+
s.handoffOccurred = true
|
|
117
|
+
s.handoffDragOffset = 0
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
s.prevScrollY = currentScrollY
|
|
121
|
+
|
|
122
|
+
let panHandles = false
|
|
123
|
+
if (!isPaneAtTop) {
|
|
124
|
+
panHandles = isDraggingDown ? currentScrollY <= 0 || !hasScrollableContent : true
|
|
125
|
+
} else if (isDraggingDown) {
|
|
126
|
+
if (currentScrollY > 0 && hasScrollableContent && !s.handoffOccurred) {
|
|
127
|
+
panHandles = false
|
|
128
|
+
} else {
|
|
129
|
+
panHandles = s.handoffOccurred || wasScrolledNowAtZero || currentScrollY <= 0
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
panHandles = !hasScrollableContent
|
|
133
|
+
if (!panHandles) {
|
|
134
|
+
s.isScrolling = true
|
|
135
|
+
scrollBridge.scrollLock = true
|
|
136
|
+
setScrollEnabled(true)
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (panHandles) {
|
|
142
|
+
setScrollEnabled(false)
|
|
143
|
+
let effectiveDragAt = dragAt
|
|
144
|
+
if (s.handoffOccurred) {
|
|
145
|
+
s.handoffDragOffset += dy
|
|
146
|
+
effectiveDragAt = s.handoffDragOffset
|
|
147
|
+
}
|
|
148
|
+
scrollBridge.drag(effectiveDragAt)
|
|
149
|
+
s.dragAt = effectiveDragAt
|
|
150
|
+
s.dys.push(dy)
|
|
151
|
+
if (s.dys.length > 100) s.dys = s.dys.slice(-10)
|
|
152
|
+
} else {
|
|
153
|
+
setScrollEnabled(true)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
onResponderRelease: release,
|
|
159
|
+
onStartShouldSetResponder,
|
|
160
|
+
onMoveShouldSetResponder,
|
|
161
|
+
onResponderMove,
|
|
162
|
+
}
|
|
163
|
+
}
|