@hanzogui/sheet 7.3.0 → 8.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/.turbo/turbo-build.log +2 -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.native.js.map +1 -1
- package/dist/cjs/SheetImplementationCustom.cjs +137 -74
- package/dist/cjs/SheetImplementationCustom.native.js +162 -83
- package/dist/cjs/SheetImplementationCustom.native.js.map +1 -1
- package/dist/cjs/SheetScrollView.cjs +155 -34
- package/dist/cjs/SheetScrollView.native.js +160 -35
- package/dist/cjs/SheetScrollView.native.js.map +1 -1
- package/dist/cjs/createSheet.cjs +7 -3
- package/dist/cjs/createSheet.native.js +7 -3
- package/dist/cjs/createSheet.native.js.map +1 -1
- package/dist/cjs/gestureState.cjs +13 -2
- package/dist/cjs/gestureState.native.js +14 -2
- package/dist/cjs/gestureState.native.js.map +1 -1
- package/dist/cjs/index.cjs +12 -1
- package/dist/cjs/index.native.js +11 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/keyboardAvoidance.cjs +93 -0
- package/dist/cjs/keyboardAvoidance.native.js +99 -0
- package/dist/cjs/keyboardAvoidance.native.js.map +1 -0
- package/dist/cjs/nativeSheet.cjs +3 -0
- package/dist/cjs/nativeSheet.native.js +3 -0
- package/dist/cjs/nativeSheet.native.js.map +1 -1
- package/dist/cjs/setupGestureHandler.cjs +9 -6
- package/dist/cjs/setupGestureHandler.native.js +11 -6
- package/dist/cjs/setupGestureHandler.native.js.map +1 -1
- package/dist/cjs/useGestureHandlerPan.cjs +86 -18
- package/dist/cjs/useGestureHandlerPan.native.js +91 -18
- package/dist/cjs/useGestureHandlerPan.native.js.map +1 -1
- package/dist/cjs/useKeyboardControllerSheet.cjs +68 -6
- package/dist/cjs/useSafeAreaInsets.cjs +44 -0
- package/dist/cjs/useSafeAreaInsets.native.js +48 -0
- package/dist/cjs/useSafeAreaInsets.native.js.map +1 -0
- package/dist/cjs/useSheetProviderProps.cjs +1 -1
- package/dist/cjs/useSheetProviderProps.native.js +1 -1
- package/dist/cjs/useSheetProviderProps.native.js.map +1 -1
- package/dist/cjs/useSheetScrollViewGestures.cjs +30 -5
- package/dist/cjs/useSheetScrollViewGestures.native.js +4 -0
- package/dist/cjs/useSheetScrollViewGestures.native.js.map +1 -1
- package/dist/cjs/webViewport.cjs +75 -0
- package/dist/cjs/webViewport.native.js +81 -0
- package/dist/cjs/webViewport.native.js.map +1 -0
- package/dist/esm/GestureDetectorWrapper.mjs.map +1 -1
- package/dist/esm/GestureDetectorWrapper.native.js.map +1 -1
- package/dist/esm/SheetImplementationCustom.mjs +143 -84
- package/dist/esm/SheetImplementationCustom.mjs.map +1 -1
- package/dist/esm/SheetImplementationCustom.native.js +173 -102
- package/dist/esm/SheetImplementationCustom.native.js.map +1 -1
- package/dist/esm/SheetScrollView.mjs +157 -36
- package/dist/esm/SheetScrollView.mjs.map +1 -1
- package/dist/esm/SheetScrollView.native.js +162 -37
- package/dist/esm/SheetScrollView.native.js.map +1 -1
- package/dist/esm/createSheet.mjs +9 -6
- package/dist/esm/createSheet.mjs.map +1 -1
- package/dist/esm/createSheet.native.js +19 -15
- package/dist/esm/createSheet.native.js.map +1 -1
- package/dist/esm/gestureState.mjs +13 -2
- package/dist/esm/gestureState.mjs.map +1 -1
- package/dist/esm/gestureState.native.js +14 -2
- package/dist/esm/gestureState.native.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -0
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/keyboardAvoidance.mjs +66 -0
- package/dist/esm/keyboardAvoidance.mjs.map +1 -0
- package/dist/esm/keyboardAvoidance.native.js +69 -0
- package/dist/esm/keyboardAvoidance.native.js.map +1 -0
- package/dist/esm/nativeSheet.mjs +3 -0
- package/dist/esm/nativeSheet.mjs.map +1 -1
- package/dist/esm/nativeSheet.native.js +3 -0
- package/dist/esm/nativeSheet.native.js.map +1 -1
- package/dist/esm/setupGestureHandler.mjs +9 -6
- package/dist/esm/setupGestureHandler.mjs.map +1 -1
- package/dist/esm/setupGestureHandler.native.js +11 -6
- package/dist/esm/setupGestureHandler.native.js.map +1 -1
- package/dist/esm/useGestureHandlerPan.mjs +87 -19
- package/dist/esm/useGestureHandlerPan.mjs.map +1 -1
- package/dist/esm/useGestureHandlerPan.native.js +92 -19
- package/dist/esm/useGestureHandlerPan.native.js.map +1 -1
- package/dist/esm/useKeyboardControllerSheet.mjs +69 -7
- package/dist/esm/useKeyboardControllerSheet.mjs.map +1 -1
- package/dist/esm/useSafeAreaInsets.mjs +7 -0
- package/dist/esm/useSafeAreaInsets.mjs.map +1 -0
- package/dist/esm/useSafeAreaInsets.native.js +8 -0
- package/dist/esm/useSafeAreaInsets.native.js.map +1 -0
- package/dist/esm/useSheetProviderProps.mjs +1 -1
- package/dist/esm/useSheetProviderProps.mjs.map +1 -1
- package/dist/esm/useSheetProviderProps.native.js +1 -1
- package/dist/esm/useSheetProviderProps.native.js.map +1 -1
- package/dist/esm/useSheetScrollViewGestures.mjs +30 -5
- package/dist/esm/useSheetScrollViewGestures.mjs.map +1 -1
- package/dist/esm/useSheetScrollViewGestures.native.js +4 -0
- package/dist/esm/useSheetScrollViewGestures.native.js.map +1 -1
- package/dist/esm/webViewport.mjs +43 -0
- package/dist/esm/webViewport.mjs.map +1 -0
- package/dist/esm/webViewport.native.js +46 -0
- package/dist/esm/webViewport.native.js.map +1 -0
- package/dist/jsx/GestureDetectorWrapper.mjs.map +1 -1
- package/dist/jsx/GestureDetectorWrapper.native.js.map +1 -1
- package/dist/jsx/SheetImplementationCustom.mjs +143 -84
- package/dist/jsx/SheetImplementationCustom.mjs.map +1 -1
- package/dist/jsx/SheetImplementationCustom.native.js +162 -83
- package/dist/jsx/SheetImplementationCustom.native.js.map +1 -1
- package/dist/jsx/SheetScrollView.mjs +157 -36
- package/dist/jsx/SheetScrollView.mjs.map +1 -1
- package/dist/jsx/SheetScrollView.native.js +160 -35
- package/dist/jsx/SheetScrollView.native.js.map +1 -1
- package/dist/jsx/createSheet.mjs +9 -6
- package/dist/jsx/createSheet.mjs.map +1 -1
- package/dist/jsx/createSheet.native.js +7 -3
- package/dist/jsx/createSheet.native.js.map +1 -1
- package/dist/jsx/gestureState.mjs +13 -2
- package/dist/jsx/gestureState.mjs.map +1 -1
- package/dist/jsx/gestureState.native.js +14 -2
- package/dist/jsx/gestureState.native.js.map +1 -1
- package/dist/jsx/index.js +2 -0
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +2 -0
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +11 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/keyboardAvoidance.mjs +66 -0
- package/dist/jsx/keyboardAvoidance.mjs.map +1 -0
- package/dist/jsx/keyboardAvoidance.native.js +99 -0
- package/dist/jsx/keyboardAvoidance.native.js.map +1 -0
- package/dist/jsx/nativeSheet.mjs +3 -0
- package/dist/jsx/nativeSheet.mjs.map +1 -1
- package/dist/jsx/nativeSheet.native.js +3 -0
- package/dist/jsx/nativeSheet.native.js.map +1 -1
- package/dist/jsx/setupGestureHandler.mjs +9 -6
- package/dist/jsx/setupGestureHandler.mjs.map +1 -1
- package/dist/jsx/setupGestureHandler.native.js +11 -6
- package/dist/jsx/setupGestureHandler.native.js.map +1 -1
- package/dist/jsx/useGestureHandlerPan.mjs +87 -19
- package/dist/jsx/useGestureHandlerPan.mjs.map +1 -1
- package/dist/jsx/useGestureHandlerPan.native.js +91 -18
- package/dist/jsx/useGestureHandlerPan.native.js.map +1 -1
- package/dist/jsx/useKeyboardControllerSheet.mjs +69 -7
- package/dist/jsx/useKeyboardControllerSheet.mjs.map +1 -1
- package/dist/jsx/useSafeAreaInsets.mjs +7 -0
- package/dist/jsx/useSafeAreaInsets.mjs.map +1 -0
- package/dist/jsx/useSafeAreaInsets.native.js +48 -0
- package/dist/jsx/useSafeAreaInsets.native.js.map +1 -0
- package/dist/jsx/useSheetProviderProps.mjs +1 -1
- package/dist/jsx/useSheetProviderProps.mjs.map +1 -1
- package/dist/jsx/useSheetProviderProps.native.js +1 -1
- package/dist/jsx/useSheetProviderProps.native.js.map +1 -1
- package/dist/jsx/useSheetScrollViewGestures.mjs +30 -5
- package/dist/jsx/useSheetScrollViewGestures.mjs.map +1 -1
- package/dist/jsx/useSheetScrollViewGestures.native.js +4 -0
- package/dist/jsx/useSheetScrollViewGestures.native.js.map +1 -1
- package/dist/jsx/webViewport.mjs +43 -0
- package/dist/jsx/webViewport.mjs.map +1 -0
- package/dist/jsx/webViewport.native.js +81 -0
- package/dist/jsx/webViewport.native.js.map +1 -0
- package/next.md +78 -0
- package/package.json +30 -48
- 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/src/GestureDetectorWrapper.tsx +0 -3
- package/src/SheetImplementationCustom.tsx +387 -127
- package/src/SheetScrollView.tsx +195 -22
- package/src/createSheet.tsx +20 -7
- package/src/gestureState.ts +22 -2
- package/src/index.ts +4 -0
- package/src/keyboardAvoidance.ts +121 -0
- package/src/nativeSheet.tsx +8 -1
- package/src/setupGestureHandler.ts +14 -5
- package/src/types.tsx +20 -1
- package/src/useGestureHandlerPan.tsx +94 -35
- package/src/useKeyboardControllerSheet.ts +127 -10
- package/src/useSafeAreaInsets.native.ts +29 -0
- package/src/useSafeAreaInsets.ts +19 -0
- package/src/useSheetProviderProps.tsx +12 -1
- package/src/useSheetScrollViewGestures.native.ts +4 -0
- package/src/useSheetScrollViewGestures.ts +52 -5
- package/src/webViewport.ts +100 -0
- package/test/keyboardAvoidance.test.ts +269 -0
- package/tsconfig.json +57 -0
- package/types/GestureDetectorWrapper.d.ts +1 -1
- package/types/GestureDetectorWrapper.d.ts.map +1 -1
- package/types/GestureSheetContext.d.ts +1 -1
- package/types/GestureSheetContext.d.ts.map +1 -1
- package/types/Sheet.d.ts +6 -5
- package/types/Sheet.d.ts.map +1 -1
- package/types/SheetContext.d.ts +5 -2
- package/types/SheetContext.d.ts.map +1 -1
- package/types/SheetController.d.ts +1 -2
- package/types/SheetController.d.ts.map +1 -1
- package/types/SheetImplementationCustom.d.ts +1 -0
- package/types/SheetImplementationCustom.d.ts.map +1 -1
- package/types/SheetScrollView.d.ts.map +1 -1
- package/types/createSheet.d.ts +13 -12
- package/types/createSheet.d.ts.map +1 -1
- package/types/gestureState.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/keyboardAvoidance.d.ts +26 -0
- package/types/keyboardAvoidance.d.ts.map +1 -0
- package/types/nativeSheet.d.ts.map +1 -1
- package/types/setupGestureHandler.d.ts.map +1 -1
- package/types/types.d.ts +10 -1
- package/types/types.d.ts.map +1 -1
- package/types/useGestureHandlerPan.d.ts +4 -1
- package/types/useGestureHandlerPan.d.ts.map +1 -1
- package/types/useKeyboardControllerSheet.d.ts +14 -3
- package/types/useKeyboardControllerSheet.d.ts.map +1 -1
- package/types/useSafeAreaInsets.d.ts +10 -0
- package/types/useSafeAreaInsets.d.ts.map +1 -0
- package/types/useSafeAreaInsets.native.d.ts +20 -0
- package/types/useSafeAreaInsets.native.d.ts.map +1 -0
- package/types/useSheetProviderProps.d.ts +3 -0
- package/types/useSheetProviderProps.d.ts.map +1 -1
- package/types/useSheetScrollViewGestures.d.ts +2 -1
- package/types/useSheetScrollViewGestures.d.ts.map +1 -1
- package/types/useSheetScrollViewGestures.native.d.ts.map +1 -1
- package/types/webViewport.d.ts +36 -0
- package/types/webViewport.d.ts.map +1 -0
- package/LICENSE +0 -42
package/src/SheetScrollView.tsx
CHANGED
|
@@ -10,6 +10,13 @@ import { getGestureHandlerState, isGestureHandlerEnabled } from './gestureState'
|
|
|
10
10
|
import { useSheetContext } from './SheetContext'
|
|
11
11
|
import type { SheetScopedProps } from './types'
|
|
12
12
|
import { useSheetScrollViewGestures } from './useSheetScrollViewGestures'
|
|
13
|
+
import {
|
|
14
|
+
getStableLayoutViewportHeight,
|
|
15
|
+
getWebKeyboardBottomInset,
|
|
16
|
+
getWebKeyboardResizeHeight,
|
|
17
|
+
isEditableElement,
|
|
18
|
+
MIN_KEYBOARD_HEIGHT,
|
|
19
|
+
} from './webViewport'
|
|
13
20
|
|
|
14
21
|
const SHEET_SCROLL_VIEW_NAME = 'SheetScrollView'
|
|
15
22
|
|
|
@@ -29,28 +36,83 @@ export const SheetScrollView = React.forwardRef<
|
|
|
29
36
|
) => {
|
|
30
37
|
const context = useSheetContext(SHEET_SCROLL_VIEW_NAME, __scopeSheet)
|
|
31
38
|
const gestureContext = useGestureSheetContext()
|
|
32
|
-
const { scrollBridge, setHasScrollView } = context
|
|
39
|
+
const { scrollBridge, setHasScrollView, hasFit, screenSize } = context
|
|
40
|
+
const keyboardOccludedHeight = Math.max(0, context.keyboardOccludedHeight || 0)
|
|
41
|
+
// OR a LIVE DOM check: context.isKeyboardVisible (React state) lags the
|
|
42
|
+
// viewport resize, so on the open-transition render this component can re-run
|
|
43
|
+
// with the shrunk consumer maxHeight BEFORE the context flag flips. reading
|
|
44
|
+
// the keyboard height straight from visualViewport closes that race so the
|
|
45
|
+
// height freeze engages on the same render that would otherwise collapse it.
|
|
46
|
+
const isKeyboardVisible =
|
|
47
|
+
context.isKeyboardVisible === true ||
|
|
48
|
+
(isWeb && getWebKeyboardResizeHeight() >= MIN_KEYBOARD_HEIGHT)
|
|
33
49
|
const [scrollEnabled] = useControllableState({
|
|
34
50
|
prop: scrollEnabledProp,
|
|
35
51
|
defaultProp: true,
|
|
36
52
|
})
|
|
37
53
|
const scrollRef = React.useRef<RNScrollView | null>(null)
|
|
38
54
|
|
|
55
|
+
const [hasScrollableContent, setHasScrollableContent] = useState(true)
|
|
56
|
+
const parentHeight = useRef(0)
|
|
57
|
+
const contentHeight = useRef(0)
|
|
58
|
+
// the sheet's authoritative pre-keyboard frame height (see SheetImpl). a
|
|
59
|
+
// scroll-view-local high-water mark used to live here, but it was unreliable
|
|
60
|
+
// (the ref could read 0 if the view remounted on focus / never laid out while
|
|
61
|
+
// closed), so the height now comes from the sheet, which doesn't remount.
|
|
62
|
+
const frozenFrameHeight = Math.max(0, context.keyboardStableFrameHeight || 0)
|
|
63
|
+
|
|
64
|
+
// with snapPointsMode="fit", Frame is content-sized (flex: 0, flex-basis: auto, height: undefined).
|
|
65
|
+
// a flex: 1 child can't grow inside a content-sized parent, so the ScrollView (and the Frame
|
|
66
|
+
// around it) collapse to 0 height. instead, let the ScrollView size to its content and cap it
|
|
67
|
+
// at the available viewport (screenSize / maxContentSize) so scrolling kicks in for tall content.
|
|
68
|
+
const fitSizingStyle = hasFit
|
|
69
|
+
? {
|
|
70
|
+
flex: undefined as undefined,
|
|
71
|
+
height: undefined as undefined,
|
|
72
|
+
maxHeight: screenSize || undefined,
|
|
73
|
+
}
|
|
74
|
+
: { flex: 1 }
|
|
75
|
+
const contentContainerStyle = hasFit ? undefined : { minHeight: '100%' as const }
|
|
76
|
+
|
|
77
|
+
// when the keyboard is open, pin the scroll view to the sheet's pre-keyboard
|
|
78
|
+
// frame height (frozenFrameHeight), overriding any consumer maxHeight. on web
|
|
79
|
+
// that maxHeight is often tied to useWindowDimensions, which SHRINKS when the
|
|
80
|
+
// keyboard opens and would otherwise collapse the sheet. holding the height
|
|
81
|
+
// constant means the web frame can translate without resizing. applied
|
|
82
|
+
// AFTER {...props} so it wins.
|
|
83
|
+
const keyboardFrozenOverride =
|
|
84
|
+
hasFit && isKeyboardVisible && frozenFrameHeight > 0
|
|
85
|
+
? { height: frozenFrameHeight, maxHeight: frozenFrameHeight }
|
|
86
|
+
: null
|
|
87
|
+
|
|
39
88
|
const panGestureRef = gestureContext?.panGestureRef
|
|
40
89
|
const { ScrollView: RNGHScrollView } = getGestureHandlerState()
|
|
41
90
|
const useRNGHScrollView = isGestureHandlerEnabled() && RNGHScrollView && panGestureRef
|
|
42
91
|
|
|
43
92
|
// RNGH scroll locking state
|
|
44
93
|
const currentScrollOffset = useRef(0)
|
|
94
|
+
const lastEmittedScrollOffset = useRef(0)
|
|
45
95
|
const lockedScrollY = useRef(0)
|
|
96
|
+
const focusedInputScrollFrame = useRef(0)
|
|
46
97
|
|
|
47
98
|
const setScrollEnabled = (next: boolean, lockTo?: number) => {
|
|
48
99
|
if (!next) {
|
|
49
|
-
const lockY = lockTo ?? currentScrollOffset.current
|
|
100
|
+
const lockY = lockTo ?? scrollBridge.scrollLockY ?? currentScrollOffset.current
|
|
101
|
+
if (scrollBridge.enabled === false && scrollBridge.scrollLockY === lockY) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
scrollBridge.enabled = false
|
|
50
105
|
lockedScrollY.current = lockY
|
|
51
106
|
scrollBridge.scrollLockY = lockY
|
|
52
|
-
|
|
107
|
+
if (currentScrollOffset.current !== lockY) {
|
|
108
|
+
scrollRef.current?.scrollTo?.({ x: 0, y: lockY, animated: false })
|
|
109
|
+
currentScrollOffset.current = lockY
|
|
110
|
+
}
|
|
53
111
|
} else {
|
|
112
|
+
if (scrollBridge.enabled === true && scrollBridge.scrollLockY === undefined) {
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
scrollBridge.enabled = true
|
|
54
116
|
lockedScrollY.current = currentScrollOffset.current
|
|
55
117
|
scrollBridge.scrollLockY = undefined
|
|
56
118
|
}
|
|
@@ -58,8 +120,97 @@ export const SheetScrollView = React.forwardRef<
|
|
|
58
120
|
|
|
59
121
|
const forceScrollTo = (y: number) => {
|
|
60
122
|
scrollRef.current?.scrollTo?.({ x: 0, y, animated: false })
|
|
123
|
+
currentScrollOffset.current = y
|
|
61
124
|
}
|
|
62
125
|
|
|
126
|
+
const emitManualScroll = React.useCallback(
|
|
127
|
+
(node: HTMLElement, y: number) => {
|
|
128
|
+
currentScrollOffset.current = y
|
|
129
|
+
scrollBridge.y = y
|
|
130
|
+
if (y > 0) scrollBridge.scrollStartY = -1
|
|
131
|
+
lastEmittedScrollOffset.current = y
|
|
132
|
+
onScroll?.({
|
|
133
|
+
nativeEvent: {
|
|
134
|
+
contentOffset: {
|
|
135
|
+
x: node.scrollLeft,
|
|
136
|
+
y,
|
|
137
|
+
},
|
|
138
|
+
contentSize: {
|
|
139
|
+
height: node.scrollHeight,
|
|
140
|
+
width: node.scrollWidth,
|
|
141
|
+
},
|
|
142
|
+
layoutMeasurement: {
|
|
143
|
+
height: node.offsetHeight,
|
|
144
|
+
width: node.offsetWidth,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
timeStamp: Date.now(),
|
|
148
|
+
} as any)
|
|
149
|
+
},
|
|
150
|
+
[onScroll, scrollBridge]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
const scrollFocusedInputClearOfKeyboard = React.useCallback(() => {
|
|
154
|
+
if (!isWeb || !hasFit || !isKeyboardVisible || keyboardOccludedHeight <= 0) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
const node = scrollRef.current?.getScrollableNode() as HTMLElement | undefined
|
|
158
|
+
const active = document.activeElement as HTMLElement | null
|
|
159
|
+
if (!node || !active || !isEditableElement(active) || !node.contains(active)) {
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const keyboardHeight = Math.max(keyboardOccludedHeight, getWebKeyboardBottomInset())
|
|
164
|
+
if (keyboardHeight <= 0) return
|
|
165
|
+
|
|
166
|
+
const activeRect = active.getBoundingClientRect()
|
|
167
|
+
const nodeRect = node.getBoundingClientRect()
|
|
168
|
+
const margin = 12
|
|
169
|
+
const keyboardTop = getStableLayoutViewportHeight() - keyboardHeight
|
|
170
|
+
const visibleTop = nodeRect.top + margin
|
|
171
|
+
const visibleBottom = Math.min(nodeRect.bottom, keyboardTop) - margin
|
|
172
|
+
let nextScrollTop = node.scrollTop
|
|
173
|
+
|
|
174
|
+
if (activeRect.bottom > visibleBottom) {
|
|
175
|
+
nextScrollTop += Math.ceil(activeRect.bottom - visibleBottom)
|
|
176
|
+
} else if (activeRect.top < visibleTop) {
|
|
177
|
+
nextScrollTop -= Math.ceil(visibleTop - activeRect.top)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const maxScrollTop = Math.max(0, node.scrollHeight - node.clientHeight)
|
|
181
|
+
nextScrollTop = Math.max(0, Math.min(maxScrollTop, nextScrollTop))
|
|
182
|
+
if (nextScrollTop === node.scrollTop) return
|
|
183
|
+
|
|
184
|
+
node.scrollTop = nextScrollTop
|
|
185
|
+
currentScrollOffset.current = nextScrollTop
|
|
186
|
+
scrollBridge.y = nextScrollTop
|
|
187
|
+
if (nextScrollTop > 0) scrollBridge.scrollStartY = -1
|
|
188
|
+
}, [hasFit, isKeyboardVisible, keyboardOccludedHeight, scrollBridge])
|
|
189
|
+
|
|
190
|
+
const scheduleFocusedInputScroll = React.useCallback(() => {
|
|
191
|
+
if (!isWeb || !hasFit) return
|
|
192
|
+
cancelAnimationFrame(focusedInputScrollFrame.current)
|
|
193
|
+
focusedInputScrollFrame.current = requestAnimationFrame(() => {
|
|
194
|
+
scrollFocusedInputClearOfKeyboard()
|
|
195
|
+
focusedInputScrollFrame.current = requestAnimationFrame(
|
|
196
|
+
scrollFocusedInputClearOfKeyboard
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
}, [hasFit, scrollFocusedInputClearOfKeyboard])
|
|
200
|
+
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
if (!isWeb || !hasFit) return
|
|
203
|
+
scheduleFocusedInputScroll()
|
|
204
|
+
window.addEventListener('focusin', scheduleFocusedInputScroll)
|
|
205
|
+
window.visualViewport?.addEventListener('resize', scheduleFocusedInputScroll)
|
|
206
|
+
|
|
207
|
+
return () => {
|
|
208
|
+
cancelAnimationFrame(focusedInputScrollFrame.current)
|
|
209
|
+
window.removeEventListener('focusin', scheduleFocusedInputScroll)
|
|
210
|
+
window.visualViewport?.removeEventListener('resize', scheduleFocusedInputScroll)
|
|
211
|
+
}
|
|
212
|
+
}, [hasFit, scheduleFocusedInputScroll])
|
|
213
|
+
|
|
63
214
|
useEffect(() => {
|
|
64
215
|
setHasScrollView(true)
|
|
65
216
|
if (isGestureHandlerEnabled()) {
|
|
@@ -73,16 +224,18 @@ export const SheetScrollView = React.forwardRef<
|
|
|
73
224
|
}
|
|
74
225
|
}, [])
|
|
75
226
|
|
|
76
|
-
const [hasScrollableContent, setHasScrollableContent] = useState(true)
|
|
77
|
-
const parentHeight = useRef(0)
|
|
78
|
-
const contentHeight = useRef(0)
|
|
79
|
-
|
|
80
227
|
const updateScrollable = () => {
|
|
81
228
|
if (parentHeight.current && contentHeight.current) {
|
|
82
229
|
setHasScrollableContent(contentHeight.current > parentHeight.current)
|
|
83
230
|
}
|
|
84
231
|
}
|
|
85
232
|
|
|
233
|
+
// track the fit height for the scrollable-content check. the keyboard-freeze
|
|
234
|
+
// height is supplied by the sheet (frozenFrameHeight), not derived here.
|
|
235
|
+
const recordFitHeight = (height: number) => {
|
|
236
|
+
parentHeight.current = height
|
|
237
|
+
}
|
|
238
|
+
|
|
86
239
|
useEffect(() => {
|
|
87
240
|
scrollBridge.hasScrollableContent = hasScrollableContent
|
|
88
241
|
}, [hasScrollableContent])
|
|
@@ -94,6 +247,7 @@ export const SheetScrollView = React.forwardRef<
|
|
|
94
247
|
hasScrollableContent,
|
|
95
248
|
scrollEnabled,
|
|
96
249
|
setScrollEnabled,
|
|
250
|
+
onManualScroll: emitManualScroll,
|
|
97
251
|
})
|
|
98
252
|
|
|
99
253
|
// content wrapper for measuring height
|
|
@@ -104,10 +258,20 @@ export const SheetScrollView = React.forwardRef<
|
|
|
104
258
|
if (height !== contentHeight.current) {
|
|
105
259
|
contentHeight.current = height
|
|
106
260
|
updateScrollable()
|
|
261
|
+
if (keyboardOccludedHeight > 0) {
|
|
262
|
+
scheduleFocusedInputScroll()
|
|
263
|
+
}
|
|
107
264
|
}
|
|
108
265
|
}}
|
|
109
266
|
>
|
|
110
267
|
{children}
|
|
268
|
+
{keyboardOccludedHeight > 0 && (
|
|
269
|
+
<View
|
|
270
|
+
data-sheet-keyboard-scroll-pad
|
|
271
|
+
height={keyboardOccludedHeight}
|
|
272
|
+
width="100%"
|
|
273
|
+
/>
|
|
274
|
+
)}
|
|
111
275
|
</View>
|
|
112
276
|
)
|
|
113
277
|
|
|
@@ -117,12 +281,12 @@ export const SheetScrollView = React.forwardRef<
|
|
|
117
281
|
return (
|
|
118
282
|
<RNGHComponent
|
|
119
283
|
ref={composeRefs(scrollRef as any, ref)}
|
|
120
|
-
style={
|
|
284
|
+
style={fitSizingStyle}
|
|
121
285
|
scrollEventThrottle={1}
|
|
122
286
|
scrollEnabled={scrollEnabled}
|
|
123
287
|
simultaneousHandlers={[panGestureRef]}
|
|
124
288
|
onLayout={(e: any) => {
|
|
125
|
-
|
|
289
|
+
recordFitHeight(Math.ceil(e.nativeEvent.layout.height))
|
|
126
290
|
updateScrollable()
|
|
127
291
|
}}
|
|
128
292
|
onScroll={(e: any) => {
|
|
@@ -137,29 +301,35 @@ export const SheetScrollView = React.forwardRef<
|
|
|
137
301
|
animated: false,
|
|
138
302
|
})
|
|
139
303
|
}
|
|
304
|
+
currentScrollOffset.current = scrollBridge.scrollLockY
|
|
140
305
|
scrollBridge.y = scrollBridge.scrollLockY
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
...e
|
|
145
|
-
|
|
146
|
-
...e.nativeEvent
|
|
147
|
-
|
|
306
|
+
if (lastEmittedScrollOffset.current !== scrollBridge.scrollLockY) {
|
|
307
|
+
lastEmittedScrollOffset.current = scrollBridge.scrollLockY
|
|
308
|
+
onScroll?.({
|
|
309
|
+
...e,
|
|
310
|
+
nativeEvent: {
|
|
311
|
+
...e.nativeEvent,
|
|
312
|
+
contentOffset: {
|
|
313
|
+
...e.nativeEvent.contentOffset,
|
|
314
|
+
y: scrollBridge.scrollLockY,
|
|
315
|
+
},
|
|
148
316
|
},
|
|
149
|
-
}
|
|
150
|
-
}
|
|
317
|
+
})
|
|
318
|
+
}
|
|
151
319
|
return
|
|
152
320
|
}
|
|
153
321
|
|
|
154
322
|
scrollBridge.y = y
|
|
155
323
|
if (y > 0) scrollBridge.scrollStartY = -1
|
|
324
|
+
lastEmittedScrollOffset.current = y
|
|
156
325
|
onScroll?.(e)
|
|
157
326
|
}}
|
|
158
|
-
contentContainerStyle={
|
|
327
|
+
contentContainerStyle={contentContainerStyle}
|
|
159
328
|
bounces={false}
|
|
160
329
|
keyboardShouldPersistTaps="always"
|
|
161
330
|
keyboardDismissMode="none"
|
|
162
331
|
{...props}
|
|
332
|
+
{...keyboardFrozenOverride}
|
|
163
333
|
>
|
|
164
334
|
{contentWrapper}
|
|
165
335
|
</RNGHComponent>
|
|
@@ -170,23 +340,26 @@ export const SheetScrollView = React.forwardRef<
|
|
|
170
340
|
return (
|
|
171
341
|
<ScrollView
|
|
172
342
|
onLayout={(e) => {
|
|
173
|
-
|
|
343
|
+
recordFitHeight(Math.ceil(e.nativeEvent.layout.height))
|
|
174
344
|
updateScrollable()
|
|
175
345
|
}}
|
|
176
346
|
ref={composeRefs(scrollRef as any, ref)}
|
|
177
|
-
|
|
347
|
+
{...fitSizingStyle}
|
|
178
348
|
scrollEventThrottle={1}
|
|
179
349
|
className="_ovs-contain"
|
|
180
350
|
scrollEnabled={scrollEnabled}
|
|
181
351
|
onScroll={(e) => {
|
|
182
352
|
const { y } = e.nativeEvent.contentOffset
|
|
353
|
+
currentScrollOffset.current = y
|
|
183
354
|
scrollBridge.y = y
|
|
184
355
|
if (y > 0) scrollBridge.scrollStartY = -1
|
|
356
|
+
lastEmittedScrollOffset.current = y
|
|
185
357
|
onScroll?.(e)
|
|
186
358
|
}}
|
|
187
|
-
contentContainerStyle={
|
|
359
|
+
contentContainerStyle={contentContainerStyle}
|
|
188
360
|
{...gestureProps}
|
|
189
361
|
{...props}
|
|
362
|
+
{...keyboardFrozenOverride}
|
|
190
363
|
>
|
|
191
364
|
{contentWrapper}
|
|
192
365
|
</ScrollView>
|
package/src/createSheet.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useComposedRefs } from '@hanzogui/compose-refs'
|
|
2
|
-
import { useIsomorphicLayoutEffect } from '@hanzogui/constants'
|
|
2
|
+
import { isWeb, useIsomorphicLayoutEffect } from '@hanzogui/constants'
|
|
3
3
|
import type {
|
|
4
4
|
GetProps,
|
|
5
5
|
ViewProps,
|
|
@@ -25,6 +25,7 @@ import { SheetScrollView } from './SheetScrollView'
|
|
|
25
25
|
import type { SheetProps, SheetScopedProps } from './types'
|
|
26
26
|
import { useSheetController } from './useSheetController'
|
|
27
27
|
import { useSheetOffscreenSize } from './useSheetOffscreenSize'
|
|
28
|
+
import { getMaxViewportHeight } from './webViewport'
|
|
28
29
|
|
|
29
30
|
type SharedSheetProps = {
|
|
30
31
|
open?: boolean
|
|
@@ -107,6 +108,7 @@ export function createSheet<
|
|
|
107
108
|
// @ts-ignore
|
|
108
109
|
<Overlay
|
|
109
110
|
{...props}
|
|
111
|
+
ref={ref}
|
|
110
112
|
onPress={composeEventHandlers(
|
|
111
113
|
props.onPress,
|
|
112
114
|
context.dismissOnOverlayPress
|
|
@@ -117,7 +119,7 @@ export function createSheet<
|
|
|
117
119
|
)}
|
|
118
120
|
/>
|
|
119
121
|
)
|
|
120
|
-
}, [props
|
|
122
|
+
}, [props, ref, context.dismissOnOverlayPress, context.setOpen])
|
|
121
123
|
|
|
122
124
|
useIsomorphicLayoutEffect(() => {
|
|
123
125
|
context.onOverlayComponent?.(element)
|
|
@@ -136,9 +138,9 @@ export function createSheet<
|
|
|
136
138
|
|
|
137
139
|
type ExtraFrameProps = {
|
|
138
140
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
141
|
+
* by default the sheet adds a view below its bottom that extends past the
|
|
142
|
+
* largest visible viewport height. this covers spring overshoot when opening
|
|
143
|
+
* so page content never shows through below the sheet.
|
|
142
144
|
*/
|
|
143
145
|
disableHideBottomOverflow?: boolean
|
|
144
146
|
|
|
@@ -225,14 +227,25 @@ export function createSheet<
|
|
|
225
227
|
<Frame
|
|
226
228
|
{...props}
|
|
227
229
|
componentName="SheetCover"
|
|
230
|
+
data-sheet-cover=""
|
|
228
231
|
children={null}
|
|
229
232
|
// Don't inherit testID - this is a visual helper element
|
|
230
233
|
testID={undefined}
|
|
231
234
|
id={undefined}
|
|
232
235
|
position="absolute"
|
|
233
|
-
bottom
|
|
236
|
+
// anchor the cover's top at the sheet's bottom edge (top: 100% of the
|
|
237
|
+
// container), then extend it downward. on web extend it past the
|
|
238
|
+
// largest viewport safari can reveal as its chrome retracts, so the
|
|
239
|
+
// sheet background covers the bottom instead of revealing the page.
|
|
240
|
+
// native keeps frameSize.
|
|
241
|
+
top="100%"
|
|
234
242
|
zIndex={-1}
|
|
235
|
-
height={
|
|
243
|
+
height={
|
|
244
|
+
isWeb
|
|
245
|
+
? Math.max(context.frameSize, getMaxViewportHeight())
|
|
246
|
+
: context.frameSize
|
|
247
|
+
}
|
|
248
|
+
maxHeight={isWeb ? 'none' : undefined}
|
|
236
249
|
left={0}
|
|
237
250
|
right={0}
|
|
238
251
|
borderWidth={0}
|
package/src/gestureState.ts
CHANGED
|
@@ -7,16 +7,36 @@ import { getGestureHandler, type GestureState } from '@hanzogui/native'
|
|
|
7
7
|
|
|
8
8
|
export type { GestureState as GestureHandlerState } from '@hanzogui/native'
|
|
9
9
|
|
|
10
|
+
const SHEET_GESTURE_STATE_KEY = '__hanzogui_sheet_gesture_state__'
|
|
11
|
+
|
|
12
|
+
function getSheetGestureHandlerState(): GestureState {
|
|
13
|
+
const g = globalThis as typeof globalThis & {
|
|
14
|
+
[SHEET_GESTURE_STATE_KEY]?: GestureState
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return g[SHEET_GESTURE_STATE_KEY] ?? getGestureHandler().state
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
// backward compat helpers
|
|
11
21
|
export function isGestureHandlerEnabled(): boolean {
|
|
12
|
-
return
|
|
22
|
+
return getSheetGestureHandlerState().enabled
|
|
13
23
|
}
|
|
14
24
|
|
|
15
25
|
export function getGestureHandlerState(): GestureState {
|
|
16
|
-
return
|
|
26
|
+
return getSheetGestureHandlerState()
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
export function setGestureHandlerState(updates: Partial<GestureState>): void {
|
|
30
|
+
const g = globalThis as typeof globalThis & {
|
|
31
|
+
[SHEET_GESTURE_STATE_KEY]?: GestureState
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sheetState = g[SHEET_GESTURE_STATE_KEY]
|
|
35
|
+
if (sheetState) {
|
|
36
|
+
Object.assign(sheetState, updates)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
getGestureHandler().set(updates)
|
|
21
41
|
}
|
|
22
42
|
|
package/src/index.ts
CHANGED
|
@@ -9,3 +9,7 @@ export * from './SheetScrollView'
|
|
|
9
9
|
export * from './nativeSheet'
|
|
10
10
|
export * from './types'
|
|
11
11
|
export * from './contexts'
|
|
12
|
+
export {
|
|
13
|
+
getGestureHandlerState as unstable_getSheetGestureHandlerState,
|
|
14
|
+
isGestureHandlerEnabled as unstable_isSheetGestureHandlerEnabled,
|
|
15
|
+
} from './gestureState'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export function getKeyboardAdjustedSheetY({
|
|
2
|
+
sheetY,
|
|
3
|
+
screenSize,
|
|
4
|
+
isKeyboardVisible,
|
|
5
|
+
keyboardHeight,
|
|
6
|
+
shouldTranslate,
|
|
7
|
+
safeAreaTop,
|
|
8
|
+
}: {
|
|
9
|
+
sheetY: number
|
|
10
|
+
screenSize: number
|
|
11
|
+
isKeyboardVisible: boolean
|
|
12
|
+
keyboardHeight: number
|
|
13
|
+
shouldTranslate: boolean
|
|
14
|
+
safeAreaTop: number
|
|
15
|
+
}) {
|
|
16
|
+
if (
|
|
17
|
+
!shouldTranslate ||
|
|
18
|
+
!isKeyboardVisible ||
|
|
19
|
+
keyboardHeight <= 0 ||
|
|
20
|
+
screenSize <= 0 ||
|
|
21
|
+
sheetY >= screenSize
|
|
22
|
+
) {
|
|
23
|
+
return sheetY
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return Math.max(safeAreaTop, sheetY - keyboardHeight)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getSheetReleasePosition({
|
|
30
|
+
positions,
|
|
31
|
+
projectedEnd,
|
|
32
|
+
currentPosition,
|
|
33
|
+
frameSize,
|
|
34
|
+
dismissOnSnapToBottom,
|
|
35
|
+
snapPointsMode,
|
|
36
|
+
isKeyboardVisible,
|
|
37
|
+
isWeb,
|
|
38
|
+
}: {
|
|
39
|
+
positions: number[]
|
|
40
|
+
projectedEnd: number
|
|
41
|
+
currentPosition: number
|
|
42
|
+
frameSize: number
|
|
43
|
+
dismissOnSnapToBottom: boolean
|
|
44
|
+
snapPointsMode: 'percent' | 'constant' | 'fit' | 'mixed'
|
|
45
|
+
isKeyboardVisible: boolean
|
|
46
|
+
isWeb: boolean
|
|
47
|
+
}) {
|
|
48
|
+
let closestPoint = 0
|
|
49
|
+
let dist = Number.POSITIVE_INFINITY
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < positions.length; i++) {
|
|
52
|
+
const position = positions[i]
|
|
53
|
+
const curDist = Math.abs(projectedEnd - position)
|
|
54
|
+
if (curDist < dist) {
|
|
55
|
+
dist = curDist
|
|
56
|
+
closestPoint = i
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// the keyboard-open dismiss threshold is a WEB keyboard-handoff fix
|
|
61
|
+
// (75de9c9694). on native the keyboard is opaque and the sheet snaps purely by
|
|
62
|
+
// projected position (the pre-rework behavior), so gate it to web only —
|
|
63
|
+
// otherwise a native fit+dismiss sheet snaps to the wrong point with the
|
|
64
|
+
// keyboard up.
|
|
65
|
+
const dismissPoint = positions.length - 1
|
|
66
|
+
const isKeyboardFitDismiss =
|
|
67
|
+
isWeb &&
|
|
68
|
+
dismissOnSnapToBottom &&
|
|
69
|
+
isKeyboardVisible &&
|
|
70
|
+
snapPointsMode === 'fit' &&
|
|
71
|
+
positions.length === 2 &&
|
|
72
|
+
closestPoint === dismissPoint
|
|
73
|
+
|
|
74
|
+
if (!isKeyboardFitDismiss) {
|
|
75
|
+
return closestPoint
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const openPosition = positions[0]
|
|
79
|
+
const dismissPosition = positions[dismissPoint]
|
|
80
|
+
const dismissDistance = dismissPosition - openPosition
|
|
81
|
+
if (dismissDistance <= 0 || frameSize <= 0) {
|
|
82
|
+
return closestPoint
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const draggedDistance = Math.max(0, currentPosition - openPosition)
|
|
86
|
+
const thresholdBase = Math.min(frameSize, dismissDistance)
|
|
87
|
+
const dismissThreshold = Math.max(120, thresholdBase * 0.35)
|
|
88
|
+
|
|
89
|
+
return draggedDistance >= dismissThreshold ? closestPoint : 0
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getKeyboardOccludedHeight({
|
|
93
|
+
frameSize,
|
|
94
|
+
isKeyboardVisible,
|
|
95
|
+
keyboardHeight,
|
|
96
|
+
screenSize,
|
|
97
|
+
sheetY,
|
|
98
|
+
}: {
|
|
99
|
+
frameSize: number
|
|
100
|
+
isKeyboardVisible: boolean
|
|
101
|
+
keyboardHeight: number
|
|
102
|
+
screenSize: number
|
|
103
|
+
sheetY: number | undefined
|
|
104
|
+
}) {
|
|
105
|
+
if (
|
|
106
|
+
!isKeyboardVisible ||
|
|
107
|
+
keyboardHeight <= 0 ||
|
|
108
|
+
screenSize <= 0 ||
|
|
109
|
+
frameSize <= 0 ||
|
|
110
|
+
sheetY === undefined ||
|
|
111
|
+
sheetY >= screenSize
|
|
112
|
+
) {
|
|
113
|
+
return 0
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const keyboardTop = screenSize - keyboardHeight
|
|
117
|
+
const sheetBottom = sheetY + frameSize
|
|
118
|
+
const occludedHeight = Math.ceil(sheetBottom - keyboardTop)
|
|
119
|
+
|
|
120
|
+
return Math.min(frameSize, Math.max(0, occludedHeight))
|
|
121
|
+
}
|
package/src/nativeSheet.tsx
CHANGED
|
@@ -59,7 +59,14 @@ export function setupNativeSheet(
|
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<>
|
|
62
|
-
<SheetProvider
|
|
62
|
+
<SheetProvider
|
|
63
|
+
setHasScrollView={emptyFn}
|
|
64
|
+
keyboardOccludedHeight={0}
|
|
65
|
+
isKeyboardVisible={false}
|
|
66
|
+
keyboardStableFrameHeight={0}
|
|
67
|
+
{...providerProps}
|
|
68
|
+
onlyShowFrame
|
|
69
|
+
>
|
|
63
70
|
<ModalSheetView ref={ref} onModalDidDismiss={() => setOpenInternal(false)}>
|
|
64
71
|
<ModalSheetViewMainContent>
|
|
65
72
|
<View style={{ flex: 1 }}>{props.children}</View>
|
|
@@ -4,8 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
import { getGestureHandler } from '@hanzogui/native'
|
|
6
6
|
|
|
7
|
+
import type { GestureState } from '@hanzogui/native'
|
|
8
|
+
|
|
9
|
+
const SHEET_GESTURE_STATE_KEY = '__hanzogui_sheet_gesture_state__'
|
|
10
|
+
|
|
7
11
|
export function isGestureHandlerEnabled(): boolean {
|
|
8
|
-
|
|
12
|
+
const g = globalThis as typeof globalThis & {
|
|
13
|
+
[SHEET_GESTURE_STATE_KEY]?: GestureState
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return g[SHEET_GESTURE_STATE_KEY]?.enabled ?? getGestureHandler().isEnabled
|
|
9
17
|
}
|
|
10
18
|
|
|
11
19
|
export interface SetupGestureHandlerConfig {
|
|
@@ -16,19 +24,20 @@ export interface SetupGestureHandlerConfig {
|
|
|
16
24
|
|
|
17
25
|
export function setupGestureHandler(config: SetupGestureHandlerConfig): void {
|
|
18
26
|
const g = globalThis as any
|
|
19
|
-
if (g.
|
|
27
|
+
if (g.__hanzogui_sheet_gesture_handler_setup) {
|
|
20
28
|
return
|
|
21
29
|
}
|
|
22
|
-
g.
|
|
30
|
+
g.__hanzogui_sheet_gesture_handler_setup = true
|
|
23
31
|
|
|
24
32
|
const { Gesture, GestureDetector, ScrollView } = config
|
|
25
33
|
|
|
26
34
|
if (Gesture && GestureDetector) {
|
|
27
|
-
|
|
35
|
+
g[SHEET_GESTURE_STATE_KEY] = {
|
|
28
36
|
enabled: true,
|
|
29
37
|
Gesture,
|
|
30
38
|
GestureDetector,
|
|
31
39
|
ScrollView: ScrollView || null,
|
|
32
|
-
|
|
40
|
+
RootView: null,
|
|
41
|
+
} satisfies GestureState
|
|
33
42
|
}
|
|
34
43
|
}
|
package/src/types.tsx
CHANGED
|
@@ -60,6 +60,12 @@ export type SheetProps = ScopedProps<
|
|
|
60
60
|
*/
|
|
61
61
|
unmountChildrenWhenHidden?: boolean
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Keep the sheet content wrapper opaque while the sheet is hidden.
|
|
65
|
+
* Useful for native visual effects that cannot initialize below a transparent ancestor.
|
|
66
|
+
*/
|
|
67
|
+
disableTransparencyHide?: boolean
|
|
68
|
+
|
|
63
69
|
/**
|
|
64
70
|
* Adapts the sheet to use native sheet on the given platform (if available)
|
|
65
71
|
*/
|
|
@@ -75,7 +81,8 @@ export type SheetProps = ScopedProps<
|
|
|
75
81
|
zIndex?: number
|
|
76
82
|
portalProps?: PortalProps
|
|
77
83
|
/**
|
|
78
|
-
*
|
|
84
|
+
* Makes the sheet move up when the mobile keyboard opens so the focused input remains visible.
|
|
85
|
+
* Works on native (via keyboard events) and on mobile web (via the VisualViewport API).
|
|
79
86
|
*/
|
|
80
87
|
moveOnKeyboardChange?: boolean
|
|
81
88
|
containerComponent?: React.ComponentType<any>
|
|
@@ -125,12 +132,24 @@ export type ScrollBridge = {
|
|
|
125
132
|
_lastTouchY?: number
|
|
126
133
|
// scroll lock position for forcing scroll back when pan handles
|
|
127
134
|
scrollLockY?: number
|
|
135
|
+
// keep scroll locked while a drag that started below the top snap is still
|
|
136
|
+
// deciding whether to hand off into content scrolling.
|
|
137
|
+
lockScrollAtTop?: boolean
|
|
128
138
|
// force scroll to position (compensates for async setNativeProps)
|
|
129
139
|
forceScrollTo?: (y: number) => void
|
|
130
140
|
// whether sheet is at top position (for scroll enable/disable)
|
|
131
141
|
isAtTop?: boolean
|
|
132
142
|
// snap sheet to a specific position (for handoff UP)
|
|
133
143
|
snapToPosition?: (positionIndex: number) => void
|
|
144
|
+
// re-baseline the pan drag origin to the current animated position. the web
|
|
145
|
+
// scroll-view hook calls this on each transition into pan ownership so a
|
|
146
|
+
// scroll→pan handoff resumes from where the sheet is, not a stale origin.
|
|
147
|
+
startPanDrag?: () => void
|
|
148
|
+
// web only: true while a touch is active on the ScrollView node. The web
|
|
149
|
+
// scroll-view gesture hook owns drag detection for those touches (it calls
|
|
150
|
+
// drag/release on this bridge), so the PanResponder must NOT also grant for
|
|
151
|
+
// them — otherwise two systems drive the animated position and it jitters.
|
|
152
|
+
scrollNodeTouched?: boolean
|
|
134
153
|
}
|
|
135
154
|
|
|
136
155
|
// keyboard controller sheet types
|