react-native-keyboard-controller 1.21.0-beta.2 → 1.21.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/README.md +1 -0
- package/android/src/base/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +1 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/ClippingScrollViewDecoratorViewManager.kt +14 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +8 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardToolbarGroupViewManager.kt +23 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ReactContext.kt +12 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputObserver.kt +2 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt +5 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardToolbarGroupViewManagerImpl.kt +13 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/modal/ModalAttachedWatcher.kt +4 -6
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +26 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/traversal/ViewHierarchyNavigator.kt +31 -4
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/ClippingScrollViewDecoratorView.kt +33 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/KeyboardToolbarGroupReactViewGroup.kt +13 -0
- package/android/src/main/jni/RNKC.h +2 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/ClippingScrollViewDecoratorViewManager.kt +16 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +9 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardToolbarGroupViewManager.kt +15 -0
- package/android/src/turbo/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +2 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCClippingScrollViewDecoratorViewComponentDescriptor.h +27 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCClippingScrollViewDecoratorViewShadowNode.cpp +14 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCClippingScrollViewDecoratorViewShadowNode.h +30 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCClippingScrollViewDecoratorViewState.h +28 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCKeyboardToolbarGroupViewComponentDescriptor.h +20 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCKeyboardToolbarGroupViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCKeyboardToolbarGroupViewShadowNode.h +23 -0
- package/common/cpp/react/renderer/components/RNKC/RNKCKeyboardToolbarGroupViewState.h +21 -0
- package/ios/KeyboardControllerModule.mm +36 -0
- package/ios/animations/KeyboardAnimation.swift +2 -2
- package/ios/animations/SpringAnimation.swift +1 -1
- package/ios/animations/TimingAnimation.swift +1 -3
- package/ios/delegates/KCTextInputCompositeDelegate.swift +2 -2
- package/ios/extensions/CGFloat.swift +1 -3
- package/ios/extensions/UIApplication.swift +1 -1
- package/ios/interactive/KeyboardAreaExtender.swift +2 -1
- package/ios/observers/FocusedInputObserver.swift +6 -3
- package/ios/observers/movement/KeyboardTrackingView.swift +4 -1
- package/ios/observers/movement/observer/KeyboardMovementObserver+Interactive.swift +2 -0
- package/ios/observers/movement/observer/KeyboardMovementObserver+Listeners.swift +30 -7
- package/ios/observers/movement/observer/KeyboardMovementObserver.swift +2 -48
- package/ios/protocols/TextInput.swift +1 -1
- package/ios/swizzling/UIResponderSwizzle.swift +2 -4
- package/ios/traversal/FocusedInputHolder.swift +3 -3
- package/ios/traversal/KeyboardView.swift +1 -1
- package/ios/traversal/ViewHierarchyNavigator.swift +51 -5
- package/ios/views/ClippingScrollViewDecoratorViewManager.h +26 -0
- package/ios/views/ClippingScrollViewDecoratorViewManager.mm +231 -0
- package/ios/views/KeyboardControllerViewManager.swift +5 -5
- package/ios/views/KeyboardToolbarGroupViewManager.h +28 -0
- package/ios/views/KeyboardToolbarGroupViewManager.mm +88 -0
- package/jest/index.js +12 -4
- package/lib/commonjs/bindings.js +18 -2
- package/lib/commonjs/bindings.js.map +1 -1
- package/lib/commonjs/bindings.native.js +3 -4
- package/lib/commonjs/bindings.native.js.map +1 -1
- package/lib/commonjs/components/KeyboardAvoidingView/index.js +30 -4
- package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +55 -14
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/components/{ChatKit → KeyboardChatScrollView}/hooks.js.map +1 -1
- package/lib/commonjs/components/KeyboardChatScrollView/index.js +111 -0
- package/lib/commonjs/components/KeyboardChatScrollView/index.js.map +1 -0
- package/lib/commonjs/components/KeyboardChatScrollView/types.js.map +1 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +221 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js.map +1 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +149 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js.map +1 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.js +240 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.js.map +1 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/types.js +6 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/types.js.map +1 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js +72 -0
- package/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js.map +1 -0
- package/lib/commonjs/components/KeyboardStickyView/index.js +3 -1
- package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/index.js +1 -0
- package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/commonjs/components/ScrollViewWithBottomPadding/index.js +36 -15
- package/lib/commonjs/components/ScrollViewWithBottomPadding/index.js.map +1 -1
- package/lib/commonjs/components/hooks/useScrollState.js +20 -1
- package/lib/commonjs/components/hooks/useScrollState.js.map +1 -1
- package/lib/commonjs/components/index.js +7 -7
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/index.js +7 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/ClippingScrollViewDecoratorViewNativeComponent.js +1 -2
- package/lib/commonjs/specs/ClippingScrollViewDecoratorViewNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/KeyboardToolbarGroupViewNativeComponent.js +12 -0
- package/lib/commonjs/specs/KeyboardToolbarGroupViewNativeComponent.js.map +1 -0
- package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
- package/lib/commonjs/types/module.js.map +1 -1
- package/lib/commonjs/types/views.js.map +1 -1
- package/lib/module/bindings.js +17 -1
- package/lib/module/bindings.js.map +1 -1
- package/lib/module/bindings.native.js +2 -3
- package/lib/module/bindings.native.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/index.js +29 -4
- package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +55 -14
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/components/{ChatKit → KeyboardChatScrollView}/hooks.js.map +1 -1
- package/lib/module/components/KeyboardChatScrollView/index.js +104 -0
- package/lib/module/components/KeyboardChatScrollView/index.js.map +1 -0
- package/lib/module/components/KeyboardChatScrollView/types.js.map +1 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +207 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js.map +1 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +143 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js.map +1 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.js +234 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.js.map +1 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/types.js +2 -0
- package/lib/module/components/KeyboardChatScrollView/useChatKeyboard/types.js.map +1 -0
- package/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js +67 -0
- package/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js.map +1 -0
- package/lib/module/components/KeyboardStickyView/index.js +3 -1
- package/lib/module/components/KeyboardStickyView/index.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/index.js +2 -1
- package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/module/components/ScrollViewWithBottomPadding/index.js +38 -17
- package/lib/module/components/ScrollViewWithBottomPadding/index.js.map +1 -1
- package/lib/module/components/hooks/useScrollState.js +21 -2
- package/lib/module/components/hooks/useScrollState.js.map +1 -1
- package/lib/module/components/index.js +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/ClippingScrollViewDecoratorViewNativeComponent.js +1 -2
- package/lib/module/specs/ClippingScrollViewDecoratorViewNativeComponent.js.map +1 -1
- package/lib/module/specs/KeyboardToolbarGroupViewNativeComponent.js +5 -0
- package/lib/module/specs/KeyboardToolbarGroupViewNativeComponent.js.map +1 -0
- package/lib/module/specs/NativeKeyboardController.js.map +1 -1
- package/lib/module/types/module.js.map +1 -1
- package/lib/module/types/views.js.map +1 -1
- package/lib/typescript/bindings.d.ts +12 -2
- package/lib/typescript/bindings.native.d.ts +2 -1
- package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +44 -13
- package/lib/typescript/components/KeyboardChatScrollView/hooks.d.ts +2 -0
- package/lib/typescript/components/KeyboardChatScrollView/index.d.ts +15 -0
- package/lib/typescript/components/{ChatKit → KeyboardChatScrollView}/types.d.ts +46 -6
- package/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts +132 -0
- package/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/index.d.ts +18 -0
- package/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/index.ios.d.ts +21 -0
- package/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts +37 -0
- package/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts +43 -0
- package/lib/typescript/components/KeyboardToolbar/index.d.ts +2 -0
- package/lib/typescript/components/ScrollViewWithBottomPadding/index.d.ts +5 -4
- package/lib/typescript/components/hooks/useScrollState.d.ts +3 -0
- package/lib/typescript/components/index.d.ts +2 -2
- package/lib/typescript/index.d.ts +2 -2
- package/lib/typescript/specs/ClippingScrollViewDecoratorViewNativeComponent.d.ts +2 -0
- package/lib/typescript/specs/KeyboardToolbarGroupViewNativeComponent.d.ts +6 -0
- package/lib/typescript/specs/NativeKeyboardController.d.ts +1 -0
- package/lib/typescript/types/module.d.ts +7 -0
- package/lib/typescript/types/views.d.ts +4 -0
- package/package.json +6 -2
- package/react-native.config.js +2 -0
- package/src/bindings.native.ts +4 -4
- package/src/bindings.ts +15 -1
- package/src/components/KeyboardAvoidingView/index.tsx +56 -29
- package/src/components/KeyboardAwareScrollView/index.tsx +68 -13
- package/src/components/KeyboardChatScrollView/hooks.ts +2 -0
- package/src/components/KeyboardChatScrollView/index.tsx +150 -0
- package/src/components/{ChatKit → KeyboardChatScrollView}/types.ts +46 -6
- package/src/components/KeyboardChatScrollView/useChatKeyboard/helpers.ts +268 -0
- package/src/components/KeyboardChatScrollView/useChatKeyboard/index.ios.ts +260 -0
- package/src/components/KeyboardChatScrollView/useChatKeyboard/index.ts +376 -0
- package/src/components/KeyboardChatScrollView/useChatKeyboard/types.ts +39 -0
- package/src/components/KeyboardChatScrollView/useExtraContentPadding/index.ts +123 -0
- package/src/components/KeyboardStickyView/index.tsx +4 -3
- package/src/components/KeyboardToolbar/index.tsx +6 -1
- package/src/components/ScrollViewWithBottomPadding/index.tsx +53 -29
- package/src/components/hooks/useScrollState.ts +24 -2
- package/src/components/index.ts +2 -2
- package/src/index.ts +2 -2
- package/src/specs/ClippingScrollViewDecoratorViewNativeComponent.ts +2 -1
- package/src/specs/KeyboardToolbarGroupViewNativeComponent.ts +10 -0
- package/src/specs/NativeKeyboardController.ts +1 -0
- package/src/types/module.ts +9 -0
- package/src/types/views.ts +4 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +0 -2
- package/ios/KeyboardController.xcodeproj/xcuserdata/kirylziusko.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/ios/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/Tests/Tests.xcodeproj/project.xcworkspace/xcuserdata/kirylziusko.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/Tests/Tests.xcodeproj/xcuserdata/kirylziusko.xcuserdatad/xcschemes/xcschememanagement.plist +0 -32
- package/lib/commonjs/components/ChatKit/TODO.md +0 -20
- package/lib/commonjs/components/ChatKit/index.js +0 -55
- package/lib/commonjs/components/ChatKit/index.js.map +0 -1
- package/lib/commonjs/components/ChatKit/types.js.map +0 -1
- package/lib/commonjs/components/ChatKit/useChatKeyboard/helpers.js +0 -101
- package/lib/commonjs/components/ChatKit/useChatKeyboard/helpers.js.map +0 -1
- package/lib/commonjs/components/ChatKit/useChatKeyboard/index.js +0 -147
- package/lib/commonjs/components/ChatKit/useChatKeyboard/index.js.map +0 -1
- package/lib/module/components/ChatKit/TODO.md +0 -20
- package/lib/module/components/ChatKit/index.js +0 -48
- package/lib/module/components/ChatKit/index.js.map +0 -1
- package/lib/module/components/ChatKit/types.js.map +0 -1
- package/lib/module/components/ChatKit/useChatKeyboard/helpers.js +0 -92
- package/lib/module/components/ChatKit/useChatKeyboard/helpers.js.map +0 -1
- package/lib/module/components/ChatKit/useChatKeyboard/index.js +0 -141
- package/lib/module/components/ChatKit/useChatKeyboard/index.js.map +0 -1
- package/lib/typescript/components/ChatKit/hooks.d.ts +0 -2
- package/lib/typescript/components/ChatKit/index.d.ts +0 -14
- package/lib/typescript/components/ChatKit/useChatKeyboard/helpers.d.ts +0 -57
- package/lib/typescript/components/ChatKit/useChatKeyboard/index.d.ts +0 -36
- package/src/components/ChatKit/TODO.md +0 -20
- package/src/components/ChatKit/hooks.ts +0 -2
- package/src/components/ChatKit/index.tsx +0 -63
- package/src/components/ChatKit/useChatKeyboard/helpers.ts +0 -118
- package/src/components/ChatKit/useChatKeyboard/index.ts +0 -228
- /package/lib/commonjs/components/{ChatKit → KeyboardChatScrollView}/hooks.js +0 -0
- /package/lib/commonjs/components/{ChatKit → KeyboardChatScrollView}/types.js +0 -0
- /package/lib/module/components/{ChatKit → KeyboardChatScrollView}/hooks.js +0 -0
- /package/lib/module/components/{ChatKit → KeyboardChatScrollView}/types.js +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.shouldShiftContent = exports.isScrollAtEnd = exports.getVisibleMinimumPaddingFraction = exports.getScrollEffective = exports.getMinimumPaddingAbsorbed = exports.getEffectiveHeight = exports.computeIOSContentOffset = exports.clampedScrollTarget = void 0;
|
|
7
|
+
var _reactNativeReanimated = require("react-native-reanimated");
|
|
8
|
+
const AT_END_THRESHOLD = 20;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Map the current keyboard height to an effective height that accounts for a
|
|
12
|
+
* fixed offset (e.g. Bottom safe-area or tab-bar height)..
|
|
13
|
+
*
|
|
14
|
+
* @param height - Current keyboard height.
|
|
15
|
+
* @param targetKeyboardHeight - Full target keyboard height (captured on keyboard open).
|
|
16
|
+
* @param offset - Fixed distance between the scroll-view bottom and the screen bottom.
|
|
17
|
+
* @returns Effective height after subtracting the offset proportionally.
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* getEffectiveHeight(300, 300, 50); // 250
|
|
21
|
+
* getEffectiveHeight(150, 300, 50); // 125
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
const getEffectiveHeight = (height, targetKeyboardHeight, offset) => {
|
|
25
|
+
"worklet";
|
|
26
|
+
|
|
27
|
+
if (offset === 0 || targetKeyboardHeight === 0) {
|
|
28
|
+
return height;
|
|
29
|
+
}
|
|
30
|
+
return (0, _reactNativeReanimated.interpolate)(height, [0, targetKeyboardHeight], [0, Math.max(targetKeyboardHeight - offset, 0)]);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check whether the scroll view is at the end of its content.
|
|
35
|
+
*
|
|
36
|
+
* For non-inverted lists the "end" is the bottom of the content.
|
|
37
|
+
* For inverted lists the "end" is the top (scroll offset near 0),
|
|
38
|
+
* because that is where the latest messages are displayed.
|
|
39
|
+
*
|
|
40
|
+
* @param scrollOffset - Current vertical scroll offset.
|
|
41
|
+
* @param layoutHeight - Visible height of the scroll view.
|
|
42
|
+
* @param contentHeight - Total height of the scrollable content.
|
|
43
|
+
* @param inverted - Whether the list is inverted.
|
|
44
|
+
* @returns `true` if the scroll position is within the threshold of the content end.
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const atEnd = isScrollAtEnd(100, 800, 920); // true (100 + 800 >= 920 - 20)
|
|
48
|
+
* const atEndInverted = isScrollAtEnd(5, 800, 2000, true); // true (5 <= 20)
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
exports.getEffectiveHeight = getEffectiveHeight;
|
|
52
|
+
const isScrollAtEnd = (scrollOffset, layoutHeight, contentHeight, inverted = false) => {
|
|
53
|
+
"worklet";
|
|
54
|
+
|
|
55
|
+
if (inverted) {
|
|
56
|
+
return scrollOffset <= AT_END_THRESHOLD;
|
|
57
|
+
}
|
|
58
|
+
return scrollOffset + layoutHeight >= contentHeight - AT_END_THRESHOLD;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Decide whether content should be shifted based on the keyboard lift behavior.
|
|
63
|
+
*
|
|
64
|
+
* @param behavior - The configured keyboard lift behavior.
|
|
65
|
+
* @param isAtEnd - Whether the scroll view is currently at the end.
|
|
66
|
+
* @returns `true` if content should be shifted.
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* shouldShiftContent("always", false); // true
|
|
70
|
+
* shouldShiftContent("whenAtEnd", false); // false
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
exports.isScrollAtEnd = isScrollAtEnd;
|
|
74
|
+
const shouldShiftContent = (behavior, isAtEnd) => {
|
|
75
|
+
"worklet";
|
|
76
|
+
|
|
77
|
+
switch (behavior) {
|
|
78
|
+
case "always":
|
|
79
|
+
return true;
|
|
80
|
+
case "never":
|
|
81
|
+
return false;
|
|
82
|
+
case "whenAtEnd":
|
|
83
|
+
return isAtEnd;
|
|
84
|
+
case "persistent":
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Compute the fraction of minimum padding space currently visible in the viewport (0–1).
|
|
91
|
+
*
|
|
92
|
+
* The minimum padding space lives in the scroll view's contentInset, NOT in the
|
|
93
|
+
* content itself. So `contentHeight` (from onContentSizeChange / scroll
|
|
94
|
+
* events) does **not** include it. The visible portion is how far the
|
|
95
|
+
* viewport extends past the content boundary into the inset area.
|
|
96
|
+
*
|
|
97
|
+
* For non-inverted lists the padding is in contentInset.bottom.
|
|
98
|
+
* For inverted lists the padding is in contentInset.top (negative scroll).
|
|
99
|
+
*
|
|
100
|
+
* @param scrollOffset - Current vertical scroll offset.
|
|
101
|
+
* @param layoutHeight - Visible height of the scroll view.
|
|
102
|
+
* @param contentHeight - Height of the scroll content (excludes insets).
|
|
103
|
+
* @param blankSpace - Size of the minimum padding inset area.
|
|
104
|
+
* @param inverted - Whether the list is inverted.
|
|
105
|
+
* @returns A value between 0 (padding fully off-screen) and 1 (padding fully visible).
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* // Non-inverted: contentHeight=1500, layout=800, blankSpace=300
|
|
109
|
+
* getVisibleMinimumPaddingFraction(1500, 800, 1500, 300, false); // 1 (at end, viewport past content)
|
|
110
|
+
* getVisibleMinimumPaddingFraction(850, 800, 1500, 300, false); // 0.5 (half padding visible)
|
|
111
|
+
* getVisibleMinimumPaddingFraction(700, 800, 1500, 300, false); // 0 (padding off-screen)
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
exports.shouldShiftContent = shouldShiftContent;
|
|
115
|
+
const getVisibleMinimumPaddingFraction = (scrollOffset, layoutHeight, contentHeight, blankSpace, inverted) => {
|
|
116
|
+
"worklet";
|
|
117
|
+
|
|
118
|
+
if (blankSpace <= 0) {
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
if (inverted) {
|
|
122
|
+
// Minimum padding is in contentInset.top; visible when scroll < 0
|
|
123
|
+
return Math.max(0, Math.min(1, -scrollOffset / blankSpace));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Minimum padding is in contentInset.bottom; visible when viewport extends past content
|
|
127
|
+
const pastContentEnd = scrollOffset + layoutHeight - contentHeight;
|
|
128
|
+
return Math.max(0, Math.min(1, pastContentEnd / blankSpace));
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Compute how much of the minimum content padding absorbs the keyboard + extraContentPadding.
|
|
133
|
+
*
|
|
134
|
+
* @param blankSpace - Minimum inset floor.
|
|
135
|
+
* @param extraContentPadding - Extra content padding from external elements.
|
|
136
|
+
* @returns The portion of blankSpace that absorbs keyboard displacement.
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* getMinimumPaddingAbsorbed(500, 20); // 480
|
|
140
|
+
* getMinimumPaddingAbsorbed(0, 20); // 0
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
exports.getVisibleMinimumPaddingFraction = getVisibleMinimumPaddingFraction;
|
|
144
|
+
const getMinimumPaddingAbsorbed = (blankSpace, extraContentPadding) => {
|
|
145
|
+
"worklet";
|
|
146
|
+
|
|
147
|
+
return Math.max(0, blankSpace - extraContentPadding);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Compute the effective scroll displacement after minimum padding absorption.
|
|
152
|
+
*
|
|
153
|
+
* @param rawEffective - Raw effective keyboard height.
|
|
154
|
+
* @param minimumPaddingAbsorbed - Amount absorbed by minimum content padding.
|
|
155
|
+
* @returns The scroll displacement after subtracting the absorbed portion.
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* getScrollEffective(300, 200); // 100
|
|
159
|
+
* getScrollEffective(300, 400); // 0
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
exports.getMinimumPaddingAbsorbed = getMinimumPaddingAbsorbed;
|
|
163
|
+
const getScrollEffective = (rawEffective, minimumPaddingAbsorbed) => {
|
|
164
|
+
"worklet";
|
|
165
|
+
|
|
166
|
+
return Math.max(0, rawEffective - minimumPaddingAbsorbed);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Compute the clamped scroll target for non-inverted lists.
|
|
171
|
+
*
|
|
172
|
+
* @param offsetBeforeScroll - Scroll position before keyboard appeared.
|
|
173
|
+
* @param keyboardHeight - Current keyboard height (used for scroll displacement).
|
|
174
|
+
* @param contentHeight - Total height of the scrollable content.
|
|
175
|
+
* @param layoutHeight - Visible height of the scroll view.
|
|
176
|
+
* @param totalPaddingForMaxScroll - Total padding to use for maxScroll calculation. When provided, used instead of keyboardHeight for the scrollable range. Defaults to keyboardHeight.
|
|
177
|
+
* @returns Clamped scroll target between 0 and maxScroll.
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* clampedScrollTarget(100, 300, 1000, 800); // 400
|
|
181
|
+
* clampedScrollTarget(100, 100, 1000, 800, 500); // 200, maxScroll uses 500
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
exports.getScrollEffective = getScrollEffective;
|
|
185
|
+
const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentHeight, layoutHeight, totalPaddingForMaxScroll) => {
|
|
186
|
+
"worklet";
|
|
187
|
+
|
|
188
|
+
const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight;
|
|
189
|
+
const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0);
|
|
190
|
+
return Math.min(Math.max(offsetBeforeScroll + keyboardHeight, 0), maxScroll);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Compute contentOffset.y for iOS lists.
|
|
195
|
+
*
|
|
196
|
+
* @param relativeScroll - Scroll position relative to current inset.
|
|
197
|
+
* @param keyboardHeight - Target keyboard height (used for scroll displacement).
|
|
198
|
+
* @param contentHeight - Total height of the scrollable content.
|
|
199
|
+
* @param layoutHeight - Visible height of the scroll view.
|
|
200
|
+
* @param inverted - Whether the list is inverted.
|
|
201
|
+
* @param totalPaddingForMaxScroll - Total padding to use for maxScroll calculation. When provided, used instead of keyboardHeight for the scrollable range. Defaults to keyboardHeight.
|
|
202
|
+
* @returns The absolute contentOffset.y to set.
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* computeIOSContentOffset(100, 300, 1000, 800, false); // 400
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
exports.clampedScrollTarget = clampedScrollTarget;
|
|
209
|
+
const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => {
|
|
210
|
+
"worklet";
|
|
211
|
+
|
|
212
|
+
const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight;
|
|
213
|
+
if (inverted) {
|
|
214
|
+
const maxScroll = Math.max(contentHeight - layoutHeight, 0);
|
|
215
|
+
return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax);
|
|
216
|
+
}
|
|
217
|
+
const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0);
|
|
218
|
+
return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll);
|
|
219
|
+
};
|
|
220
|
+
exports.computeIOSContentOffset = computeIOSContentOffset;
|
|
221
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNativeReanimated","require","AT_END_THRESHOLD","getEffectiveHeight","height","targetKeyboardHeight","offset","interpolate","Math","max","exports","isScrollAtEnd","scrollOffset","layoutHeight","contentHeight","inverted","shouldShiftContent","behavior","isAtEnd","getVisibleMinimumPaddingFraction","blankSpace","min","pastContentEnd","getMinimumPaddingAbsorbed","extraContentPadding","getScrollEffective","rawEffective","minimumPaddingAbsorbed","clampedScrollTarget","offsetBeforeScroll","keyboardHeight","totalPaddingForMaxScroll","paddingForMax","undefined","maxScroll","computeIOSContentOffset","relativeScroll"],"sources":["helpers.ts"],"sourcesContent":["import { interpolate } from \"react-native-reanimated\";\n\nimport type { KeyboardLiftBehavior } from \"./types\";\n\nconst AT_END_THRESHOLD = 20;\n\n/**\n * Map the current keyboard height to an effective height that accounts for a\n * fixed offset (e.g. Bottom safe-area or tab-bar height)..\n *\n * @param height - Current keyboard height.\n * @param targetKeyboardHeight - Full target keyboard height (captured on keyboard open).\n * @param offset - Fixed distance between the scroll-view bottom and the screen bottom.\n * @returns Effective height after subtracting the offset proportionally.\n * @example\n * ```ts\n * getEffectiveHeight(300, 300, 50); // 250\n * getEffectiveHeight(150, 300, 50); // 125\n * ```\n */\nexport const getEffectiveHeight = (\n height: number,\n targetKeyboardHeight: number,\n offset: number,\n): number => {\n \"worklet\";\n\n if (offset === 0 || targetKeyboardHeight === 0) {\n return height;\n }\n\n return interpolate(\n height,\n [0, targetKeyboardHeight],\n [0, Math.max(targetKeyboardHeight - offset, 0)],\n );\n};\n\n/**\n * Check whether the scroll view is at the end of its content.\n *\n * For non-inverted lists the \"end\" is the bottom of the content.\n * For inverted lists the \"end\" is the top (scroll offset near 0),\n * because that is where the latest messages are displayed.\n *\n * @param scrollOffset - Current vertical scroll offset.\n * @param layoutHeight - Visible height of the scroll view.\n * @param contentHeight - Total height of the scrollable content.\n * @param inverted - Whether the list is inverted.\n * @returns `true` if the scroll position is within the threshold of the content end.\n * @example\n * ```ts\n * const atEnd = isScrollAtEnd(100, 800, 920); // true (100 + 800 >= 920 - 20)\n * const atEndInverted = isScrollAtEnd(5, 800, 2000, true); // true (5 <= 20)\n * ```\n */\nexport const isScrollAtEnd = (\n scrollOffset: number,\n layoutHeight: number,\n contentHeight: number,\n inverted: boolean = false,\n): boolean => {\n \"worklet\";\n\n if (inverted) {\n return scrollOffset <= AT_END_THRESHOLD;\n }\n\n return scrollOffset + layoutHeight >= contentHeight - AT_END_THRESHOLD;\n};\n\n/**\n * Decide whether content should be shifted based on the keyboard lift behavior.\n *\n * @param behavior - The configured keyboard lift behavior.\n * @param isAtEnd - Whether the scroll view is currently at the end.\n * @returns `true` if content should be shifted.\n * @example\n * ```ts\n * shouldShiftContent(\"always\", false); // true\n * shouldShiftContent(\"whenAtEnd\", false); // false\n * ```\n */\nexport const shouldShiftContent = (\n behavior: KeyboardLiftBehavior,\n isAtEnd: boolean,\n): boolean => {\n \"worklet\";\n\n switch (behavior) {\n case \"always\":\n return true;\n case \"never\":\n return false;\n case \"whenAtEnd\":\n return isAtEnd;\n case \"persistent\":\n return true;\n }\n};\n\n/**\n * Compute the fraction of minimum padding space currently visible in the viewport (0–1).\n *\n * The minimum padding space lives in the scroll view's contentInset, NOT in the\n * content itself. So `contentHeight` (from onContentSizeChange / scroll\n * events) does **not** include it. The visible portion is how far the\n * viewport extends past the content boundary into the inset area.\n *\n * For non-inverted lists the padding is in contentInset.bottom.\n * For inverted lists the padding is in contentInset.top (negative scroll).\n *\n * @param scrollOffset - Current vertical scroll offset.\n * @param layoutHeight - Visible height of the scroll view.\n * @param contentHeight - Height of the scroll content (excludes insets).\n * @param blankSpace - Size of the minimum padding inset area.\n * @param inverted - Whether the list is inverted.\n * @returns A value between 0 (padding fully off-screen) and 1 (padding fully visible).\n * @example\n * ```ts\n * // Non-inverted: contentHeight=1500, layout=800, blankSpace=300\n * getVisibleMinimumPaddingFraction(1500, 800, 1500, 300, false); // 1 (at end, viewport past content)\n * getVisibleMinimumPaddingFraction(850, 800, 1500, 300, false); // 0.5 (half padding visible)\n * getVisibleMinimumPaddingFraction(700, 800, 1500, 300, false); // 0 (padding off-screen)\n * ```\n */\nexport const getVisibleMinimumPaddingFraction = (\n scrollOffset: number,\n layoutHeight: number,\n contentHeight: number,\n blankSpace: number,\n inverted: boolean,\n): number => {\n \"worklet\";\n\n if (blankSpace <= 0) {\n return 0;\n }\n\n if (inverted) {\n // Minimum padding is in contentInset.top; visible when scroll < 0\n return Math.max(0, Math.min(1, -scrollOffset / blankSpace));\n }\n\n // Minimum padding is in contentInset.bottom; visible when viewport extends past content\n const pastContentEnd = scrollOffset + layoutHeight - contentHeight;\n\n return Math.max(0, Math.min(1, pastContentEnd / blankSpace));\n};\n\n/**\n * Compute how much of the minimum content padding absorbs the keyboard + extraContentPadding.\n *\n * @param blankSpace - Minimum inset floor.\n * @param extraContentPadding - Extra content padding from external elements.\n * @returns The portion of blankSpace that absorbs keyboard displacement.\n * @example\n * ```ts\n * getMinimumPaddingAbsorbed(500, 20); // 480\n * getMinimumPaddingAbsorbed(0, 20); // 0\n * ```\n */\nexport const getMinimumPaddingAbsorbed = (\n blankSpace: number,\n extraContentPadding: number,\n): number => {\n \"worklet\";\n\n return Math.max(0, blankSpace - extraContentPadding);\n};\n\n/**\n * Compute the effective scroll displacement after minimum padding absorption.\n *\n * @param rawEffective - Raw effective keyboard height.\n * @param minimumPaddingAbsorbed - Amount absorbed by minimum content padding.\n * @returns The scroll displacement after subtracting the absorbed portion.\n * @example\n * ```ts\n * getScrollEffective(300, 200); // 100\n * getScrollEffective(300, 400); // 0\n * ```\n */\nexport const getScrollEffective = (\n rawEffective: number,\n minimumPaddingAbsorbed: number,\n): number => {\n \"worklet\";\n\n return Math.max(0, rawEffective - minimumPaddingAbsorbed);\n};\n\n/**\n * Compute the clamped scroll target for non-inverted lists.\n *\n * @param offsetBeforeScroll - Scroll position before keyboard appeared.\n * @param keyboardHeight - Current keyboard height (used for scroll displacement).\n * @param contentHeight - Total height of the scrollable content.\n * @param layoutHeight - Visible height of the scroll view.\n * @param totalPaddingForMaxScroll - Total padding to use for maxScroll calculation. When provided, used instead of keyboardHeight for the scrollable range. Defaults to keyboardHeight.\n * @returns Clamped scroll target between 0 and maxScroll.\n * @example\n * ```ts\n * clampedScrollTarget(100, 300, 1000, 800); // 400\n * clampedScrollTarget(100, 100, 1000, 800, 500); // 200, maxScroll uses 500\n * ```\n */\nexport const clampedScrollTarget = (\n offsetBeforeScroll: number,\n keyboardHeight: number,\n contentHeight: number,\n layoutHeight: number,\n totalPaddingForMaxScroll?: number,\n): number => {\n \"worklet\";\n\n const paddingForMax =\n totalPaddingForMaxScroll !== undefined\n ? totalPaddingForMaxScroll\n : keyboardHeight;\n const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0);\n\n return Math.min(Math.max(offsetBeforeScroll + keyboardHeight, 0), maxScroll);\n};\n\n/**\n * Compute contentOffset.y for iOS lists.\n *\n * @param relativeScroll - Scroll position relative to current inset.\n * @param keyboardHeight - Target keyboard height (used for scroll displacement).\n * @param contentHeight - Total height of the scrollable content.\n * @param layoutHeight - Visible height of the scroll view.\n * @param inverted - Whether the list is inverted.\n * @param totalPaddingForMaxScroll - Total padding to use for maxScroll calculation. When provided, used instead of keyboardHeight for the scrollable range. Defaults to keyboardHeight.\n * @returns The absolute contentOffset.y to set.\n * @example\n * ```ts\n * computeIOSContentOffset(100, 300, 1000, 800, false); // 400\n * ```\n */\nexport const computeIOSContentOffset = (\n relativeScroll: number,\n keyboardHeight: number,\n contentHeight: number,\n layoutHeight: number,\n inverted: boolean,\n totalPaddingForMaxScroll?: number,\n): number => {\n \"worklet\";\n\n const paddingForMax =\n totalPaddingForMaxScroll !== undefined\n ? totalPaddingForMaxScroll\n : keyboardHeight;\n\n if (inverted) {\n const maxScroll = Math.max(contentHeight - layoutHeight, 0);\n\n return Math.max(\n Math.min(relativeScroll - keyboardHeight, maxScroll),\n -paddingForMax,\n );\n }\n\n const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0);\n\n return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll);\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useChatKeyboard = useChatKeyboard;
|
|
7
|
+
var _reactNativeReanimated = require("react-native-reanimated");
|
|
8
|
+
var _hooks = require("../../../hooks");
|
|
9
|
+
var _useScrollState = _interopRequireDefault(require("../../hooks/useScrollState"));
|
|
10
|
+
var _helpers = require("./helpers");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
/**
|
|
13
|
+
* Hook that manages keyboard-driven scrolling for chat-style scroll views.
|
|
14
|
+
* Calculates padding (extra scrollable space) and content shift values,
|
|
15
|
+
* using iOS-specific strategy (contentOffset set once in onStart).
|
|
16
|
+
*
|
|
17
|
+
* @param scrollViewRef - Animated ref to the scroll view.
|
|
18
|
+
* @param options - Configuration for inverted and keyboardLiftBehavior.
|
|
19
|
+
* @returns Shared values for padding and contentOffsetY.
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const { padding, contentOffsetY } = useChatKeyboard(ref, {
|
|
23
|
+
* inverted: false,
|
|
24
|
+
* keyboardLiftBehavior: "always",
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function useChatKeyboard(scrollViewRef, options) {
|
|
29
|
+
const {
|
|
30
|
+
inverted,
|
|
31
|
+
keyboardLiftBehavior,
|
|
32
|
+
freeze,
|
|
33
|
+
offset,
|
|
34
|
+
blankSpace,
|
|
35
|
+
extraContentPadding
|
|
36
|
+
} = options;
|
|
37
|
+
const padding = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
38
|
+
const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
39
|
+
const contentOffsetY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
40
|
+
const targetKeyboardHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
41
|
+
const prevAbsorption = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
42
|
+
const {
|
|
43
|
+
layout,
|
|
44
|
+
size,
|
|
45
|
+
offset: scroll,
|
|
46
|
+
onLayout,
|
|
47
|
+
onContentSizeChange
|
|
48
|
+
} = (0, _useScrollState.default)(scrollViewRef);
|
|
49
|
+
(0, _hooks.useKeyboardHandler)({
|
|
50
|
+
onStart: e => {
|
|
51
|
+
"worklet";
|
|
52
|
+
|
|
53
|
+
if (freeze) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (e.height > 0) {
|
|
57
|
+
// eslint-disable-next-line react-compiler/react-compiler
|
|
58
|
+
targetKeyboardHeight.value = e.height;
|
|
59
|
+
}
|
|
60
|
+
const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);
|
|
61
|
+
const atEnd = (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted);
|
|
62
|
+
|
|
63
|
+
// Scale minimum padding absorption by how much of it is visible.
|
|
64
|
+
// Fully visible → full absorption; fully off-screen → no absorption.
|
|
65
|
+
const visibleFraction = (0, _helpers.getVisibleMinimumPaddingFraction)(scroll.value, layout.value.height, size.value.height, blankSpace.value, inverted);
|
|
66
|
+
const visiblePadding = visibleFraction * blankSpace.value;
|
|
67
|
+
const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value);
|
|
68
|
+
const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed);
|
|
69
|
+
const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);
|
|
70
|
+
|
|
71
|
+
// persistent mode: when keyboard shrinks, clamp to valid range
|
|
72
|
+
if (keyboardLiftBehavior === "persistent" && effective < padding.value) {
|
|
73
|
+
padding.value = effective;
|
|
74
|
+
prevAbsorption.value = minimumPaddingAbsorbed;
|
|
75
|
+
if (inverted) {
|
|
76
|
+
const maxScroll = Math.max(size.value.height - layout.value.height, 0);
|
|
77
|
+
contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll));
|
|
78
|
+
} else {
|
|
79
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);
|
|
80
|
+
contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// never mode: when keyboard shrinks, clamp to valid range
|
|
86
|
+
// to avoid ghost padding
|
|
87
|
+
if (keyboardLiftBehavior === "never" && effective < padding.value && atEnd) {
|
|
88
|
+
padding.value = effective;
|
|
89
|
+
prevAbsorption.value = minimumPaddingAbsorbed;
|
|
90
|
+
if (inverted) {
|
|
91
|
+
const maxScroll = Math.max(size.value.height - layout.value.height, 0);
|
|
92
|
+
contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll));
|
|
93
|
+
} else {
|
|
94
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);
|
|
95
|
+
contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Undo only the scroll displacement that was actually applied
|
|
101
|
+
// (not the full padding, which includes the absorbed portion).
|
|
102
|
+
// Use the stored absorption from the previous event so that
|
|
103
|
+
// the unwind matches the shift that was originally applied.
|
|
104
|
+
const prevScrollEffective = (0, _helpers.getScrollEffective)(padding.value, prevAbsorption.value);
|
|
105
|
+
const relativeScroll = inverted ? scroll.value + prevScrollEffective : scroll.value - prevScrollEffective;
|
|
106
|
+
padding.value = effective;
|
|
107
|
+
prevAbsorption.value = minimumPaddingAbsorbed;
|
|
108
|
+
if (!(0, _helpers.shouldShiftContent)(keyboardLiftBehavior, atEnd)) {
|
|
109
|
+
// Preserve current scroll position so animated props
|
|
110
|
+
// don't re-apply a stale contentOffset when padding changes
|
|
111
|
+
contentOffsetY.value = scroll.value;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// When blankSpace fully absorbs the keyboard opening, preserve current scroll position
|
|
116
|
+
// (only when keyboard is open — effective > 0 — not when closing)
|
|
117
|
+
if (scrollEffective === 0 && minimumPaddingAbsorbed > 0 && effective > 0) {
|
|
118
|
+
contentOffsetY.value = scroll.value;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding);
|
|
122
|
+
},
|
|
123
|
+
onMove: () => {
|
|
124
|
+
"worklet";
|
|
125
|
+
|
|
126
|
+
// iOS doesn't need per-frame updates (contentOffset handles it)
|
|
127
|
+
},
|
|
128
|
+
onEnd: e => {
|
|
129
|
+
"worklet";
|
|
130
|
+
|
|
131
|
+
if (freeze) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);
|
|
135
|
+
padding.value = effective;
|
|
136
|
+
}
|
|
137
|
+
}, [inverted, keyboardLiftBehavior, freeze, offset, extraContentPadding]);
|
|
138
|
+
return {
|
|
139
|
+
padding,
|
|
140
|
+
currentHeight,
|
|
141
|
+
contentOffsetY,
|
|
142
|
+
scroll,
|
|
143
|
+
layout,
|
|
144
|
+
size,
|
|
145
|
+
onLayout,
|
|
146
|
+
onContentSizeChange
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=index.ios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNativeReanimated","require","_hooks","_useScrollState","_interopRequireDefault","_helpers","e","__esModule","default","useChatKeyboard","scrollViewRef","options","inverted","keyboardLiftBehavior","freeze","offset","blankSpace","extraContentPadding","padding","useSharedValue","currentHeight","contentOffsetY","targetKeyboardHeight","prevAbsorption","layout","size","scroll","onLayout","onContentSizeChange","useScrollState","useKeyboardHandler","onStart","height","value","effective","getEffectiveHeight","atEnd","isScrollAtEnd","visibleFraction","getVisibleMinimumPaddingFraction","visiblePadding","minimumPaddingAbsorbed","Math","max","scrollEffective","getScrollEffective","actualTotalPadding","maxScroll","min","prevScrollEffective","relativeScroll","shouldShiftContent","computeIOSContentOffset","onMove","onEnd"],"sources":["index.ios.ts"],"sourcesContent":["import { useSharedValue } from \"react-native-reanimated\";\n\nimport { useKeyboardHandler } from \"../../../hooks\";\nimport useScrollState from \"../../hooks/useScrollState\";\n\nimport {\n computeIOSContentOffset,\n getEffectiveHeight,\n getScrollEffective,\n getVisibleMinimumPaddingFraction,\n isScrollAtEnd,\n shouldShiftContent,\n} from \"./helpers\";\n\nimport type { UseChatKeyboardOptions, UseChatKeyboardReturn } from \"./types\";\nimport type { AnimatedRef } from \"react-native-reanimated\";\nimport type Reanimated from \"react-native-reanimated\";\n\n/**\n * Hook that manages keyboard-driven scrolling for chat-style scroll views.\n * Calculates padding (extra scrollable space) and content shift values,\n * using iOS-specific strategy (contentOffset set once in onStart).\n *\n * @param scrollViewRef - Animated ref to the scroll view.\n * @param options - Configuration for inverted and keyboardLiftBehavior.\n * @returns Shared values for padding and contentOffsetY.\n * @example\n * ```tsx\n * const { padding, contentOffsetY } = useChatKeyboard(ref, {\n * inverted: false,\n * keyboardLiftBehavior: \"always\",\n * });\n * ```\n */\nfunction useChatKeyboard(\n scrollViewRef: AnimatedRef<Reanimated.ScrollView>,\n options: UseChatKeyboardOptions,\n): UseChatKeyboardReturn {\n const {\n inverted,\n keyboardLiftBehavior,\n freeze,\n offset,\n blankSpace,\n extraContentPadding,\n } = options;\n\n const padding = useSharedValue(0);\n const currentHeight = useSharedValue(0);\n const contentOffsetY = useSharedValue(0);\n const targetKeyboardHeight = useSharedValue(0);\n const prevAbsorption = useSharedValue(0);\n\n const {\n layout,\n size,\n offset: scroll,\n onLayout,\n onContentSizeChange,\n } = useScrollState(scrollViewRef);\n\n useKeyboardHandler(\n {\n onStart: (e) => {\n \"worklet\";\n\n if (freeze) {\n return;\n }\n\n if (e.height > 0) {\n // eslint-disable-next-line react-compiler/react-compiler\n targetKeyboardHeight.value = e.height;\n }\n\n const effective = getEffectiveHeight(\n e.height,\n targetKeyboardHeight.value,\n offset,\n );\n\n const atEnd = isScrollAtEnd(\n scroll.value,\n layout.value.height,\n size.value.height,\n inverted,\n );\n\n // Scale minimum padding absorption by how much of it is visible.\n // Fully visible → full absorption; fully off-screen → no absorption.\n const visibleFraction = getVisibleMinimumPaddingFraction(\n scroll.value,\n layout.value.height,\n size.value.height,\n blankSpace.value,\n inverted,\n );\n const visiblePadding = visibleFraction * blankSpace.value;\n const minimumPaddingAbsorbed = Math.max(\n 0,\n visiblePadding - extraContentPadding.value,\n );\n const scrollEffective = getScrollEffective(\n effective,\n minimumPaddingAbsorbed,\n );\n const actualTotalPadding = Math.max(\n blankSpace.value,\n effective + extraContentPadding.value,\n );\n\n // persistent mode: when keyboard shrinks, clamp to valid range\n if (\n keyboardLiftBehavior === \"persistent\" &&\n effective < padding.value\n ) {\n padding.value = effective;\n prevAbsorption.value = minimumPaddingAbsorbed;\n\n if (inverted) {\n const maxScroll = Math.max(\n size.value.height - layout.value.height,\n 0,\n );\n\n contentOffsetY.value = Math.max(\n -actualTotalPadding,\n Math.min(scroll.value, maxScroll),\n );\n } else {\n const maxScroll = Math.max(\n size.value.height - layout.value.height + actualTotalPadding,\n 0,\n );\n\n contentOffsetY.value = Math.max(\n 0,\n Math.min(scroll.value, maxScroll),\n );\n }\n\n return;\n }\n\n // never mode: when keyboard shrinks, clamp to valid range\n // to avoid ghost padding\n if (\n keyboardLiftBehavior === \"never\" &&\n effective < padding.value &&\n atEnd\n ) {\n padding.value = effective;\n prevAbsorption.value = minimumPaddingAbsorbed;\n\n if (inverted) {\n const maxScroll = Math.max(\n size.value.height - layout.value.height,\n 0,\n );\n\n contentOffsetY.value = Math.max(\n -actualTotalPadding,\n Math.min(scroll.value, maxScroll),\n );\n } else {\n const maxScroll = Math.max(\n size.value.height - layout.value.height + actualTotalPadding,\n 0,\n );\n\n contentOffsetY.value = Math.max(\n 0,\n Math.min(scroll.value, maxScroll),\n );\n }\n\n return;\n }\n\n // Undo only the scroll displacement that was actually applied\n // (not the full padding, which includes the absorbed portion).\n // Use the stored absorption from the previous event so that\n // the unwind matches the shift that was originally applied.\n const prevScrollEffective = getScrollEffective(\n padding.value,\n prevAbsorption.value,\n );\n const relativeScroll = inverted\n ? scroll.value + prevScrollEffective\n : scroll.value - prevScrollEffective;\n\n padding.value = effective;\n prevAbsorption.value = minimumPaddingAbsorbed;\n\n if (!shouldShiftContent(keyboardLiftBehavior, atEnd)) {\n // Preserve current scroll position so animated props\n // don't re-apply a stale contentOffset when padding changes\n contentOffsetY.value = scroll.value;\n\n return;\n }\n\n // When blankSpace fully absorbs the keyboard opening, preserve current scroll position\n // (only when keyboard is open — effective > 0 — not when closing)\n if (\n scrollEffective === 0 &&\n minimumPaddingAbsorbed > 0 &&\n effective > 0\n ) {\n contentOffsetY.value = scroll.value;\n\n return;\n }\n\n contentOffsetY.value = computeIOSContentOffset(\n relativeScroll,\n scrollEffective,\n size.value.height,\n layout.value.height,\n inverted,\n actualTotalPadding,\n );\n },\n onMove: () => {\n \"worklet\";\n\n // iOS doesn't need per-frame updates (contentOffset handles it)\n },\n onEnd: (e) => {\n \"worklet\";\n\n if (freeze) {\n return;\n }\n\n const effective = getEffectiveHeight(\n e.height,\n targetKeyboardHeight.value,\n offset,\n );\n\n padding.value = effective;\n },\n },\n [inverted, keyboardLiftBehavior, freeze, offset, extraContentPadding],\n );\n\n return {\n padding,\n currentHeight,\n contentOffsetY,\n scroll,\n layout,\n size,\n onLayout,\n onContentSizeChange,\n };\n}\n\nexport { useChatKeyboard };\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useChatKeyboard = useChatKeyboard;
|
|
7
|
+
var _reactNativeReanimated = require("react-native-reanimated");
|
|
8
|
+
var _hooks = require("../../../hooks");
|
|
9
|
+
var _useScrollState = _interopRequireDefault(require("../../hooks/useScrollState"));
|
|
10
|
+
var _helpers = require("./helpers");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
/**
|
|
13
|
+
* Hook that manages keyboard-driven scrolling for chat-style scroll views.
|
|
14
|
+
* Calculates padding (extra scrollable space) and content shift values,
|
|
15
|
+
* using per-frame scrollTo updates (Android and other platforms).
|
|
16
|
+
*
|
|
17
|
+
* @param scrollViewRef - Animated ref to the scroll view.
|
|
18
|
+
* @param options - Configuration for inverted and keyboardLiftBehavior.
|
|
19
|
+
* @returns Shared values for padding and contentOffsetY (always `undefined`).
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const { padding } = useChatKeyboard(ref, { inverted: false, keyboardLiftBehavior: "always" });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function useChatKeyboard(scrollViewRef, options) {
|
|
26
|
+
const {
|
|
27
|
+
inverted,
|
|
28
|
+
keyboardLiftBehavior,
|
|
29
|
+
freeze,
|
|
30
|
+
offset,
|
|
31
|
+
blankSpace,
|
|
32
|
+
extraContentPadding
|
|
33
|
+
} = options;
|
|
34
|
+
const padding = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
35
|
+
const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
36
|
+
const offsetBeforeScroll = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
37
|
+
const targetKeyboardHeight = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
38
|
+
const closing = (0, _reactNativeReanimated.useSharedValue)(false);
|
|
39
|
+
const minimumPaddingFractionOnOpen = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
40
|
+
const actualOpenShift = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
41
|
+
const {
|
|
42
|
+
layout,
|
|
43
|
+
size,
|
|
44
|
+
offset: scroll,
|
|
45
|
+
onLayout,
|
|
46
|
+
onContentSizeChange
|
|
47
|
+
} = (0, _useScrollState.default)(scrollViewRef);
|
|
48
|
+
const clampScrollIfNeeded = (effective, totalPaddingForMaxScroll) => {
|
|
49
|
+
"worklet";
|
|
50
|
+
|
|
51
|
+
const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : effective;
|
|
52
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + paddingForMax, 0);
|
|
53
|
+
if (scroll.value > maxScroll) {
|
|
54
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, maxScroll, false);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
(0, _hooks.useKeyboardHandler)({
|
|
58
|
+
onStart: e => {
|
|
59
|
+
"worklet";
|
|
60
|
+
|
|
61
|
+
if (freeze) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (e.height > 0) {
|
|
65
|
+
// eslint-disable-next-line react-compiler/react-compiler
|
|
66
|
+
targetKeyboardHeight.value = e.height;
|
|
67
|
+
closing.value = false;
|
|
68
|
+
} else {
|
|
69
|
+
closing.value = true;
|
|
70
|
+
}
|
|
71
|
+
const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);
|
|
72
|
+
const atEnd = (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted);
|
|
73
|
+
|
|
74
|
+
// Scale minimum padding absorption by how much of it is visible.
|
|
75
|
+
// Fully visible → full absorption; fully off-screen → no absorption.
|
|
76
|
+
const visibleFraction = (0, _helpers.getVisibleMinimumPaddingFraction)(scroll.value, layout.value.height, size.value.height, blankSpace.value, inverted);
|
|
77
|
+
const minimumPaddingAbsorbed = visibleFraction >= 1 ? (0, _helpers.getMinimumPaddingAbsorbed)(blankSpace.value, extraContentPadding.value) : 0;
|
|
78
|
+
const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed);
|
|
79
|
+
if (inverted && e.duration === -1) {
|
|
80
|
+
// Android inverted: skip post-interactive snap-back events
|
|
81
|
+
// (duration === -1 means the keyboard is re-establishing its
|
|
82
|
+
// position after an interactive gesture, not a real animation)
|
|
83
|
+
return;
|
|
84
|
+
} else if (e.height > 0) {
|
|
85
|
+
// Android: keyboard opening — set padding + capture scroll position
|
|
86
|
+
minimumPaddingFractionOnOpen.value = visibleFraction >= 1 ? 1 : 0;
|
|
87
|
+
padding.value = effective;
|
|
88
|
+
offsetBeforeScroll.value = scroll.value;
|
|
89
|
+
if (!inverted && keyboardLiftBehavior === "whenAtEnd" && !atEnd) {
|
|
90
|
+
// Sentinel: don't scroll in onMove (non-inverted only)
|
|
91
|
+
offsetBeforeScroll.value = -1;
|
|
92
|
+
} else if (!inverted && scrollEffective === 0) {
|
|
93
|
+
// blankSpace fully absorbs the keyboard — prevent scroll
|
|
94
|
+
offsetBeforeScroll.value = -1;
|
|
95
|
+
} else if (inverted && scrollEffective === 0) {
|
|
96
|
+
// blankSpace fully absorbs the keyboard — guard for inverted
|
|
97
|
+
offsetBeforeScroll.value = scroll.value;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
// Android: keyboard closing — re-capture scroll position
|
|
101
|
+
if (inverted) {
|
|
102
|
+
offsetBeforeScroll.value = scroll.value;
|
|
103
|
+
} else {
|
|
104
|
+
// Preserve "whenAtEnd" sentinel: if open didn't shift, close shouldn't either
|
|
105
|
+
if (offsetBeforeScroll.value !== -1) {
|
|
106
|
+
// Use the actual displacement recorded at end of open animation
|
|
107
|
+
// (not the theoretical value) so close is symmetric with open
|
|
108
|
+
offsetBeforeScroll.value = scroll.value - actualOpenShift.value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
onMove: e => {
|
|
114
|
+
"worklet";
|
|
115
|
+
|
|
116
|
+
if (freeze) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
currentHeight.value = e.height;
|
|
120
|
+
if (inverted) {
|
|
121
|
+
// Skip post-interactive snap-back (duration === -1)
|
|
122
|
+
if (e.duration === -1) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);
|
|
126
|
+
const minimumPaddingAbsorbed = (0, _helpers.getMinimumPaddingAbsorbed)(blankSpace.value, extraContentPadding.value) * minimumPaddingFractionOnOpen.value;
|
|
127
|
+
const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed);
|
|
128
|
+
const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);
|
|
129
|
+
|
|
130
|
+
// Check if we should shift content based on position when keyboard started
|
|
131
|
+
const wasAtEnd = (0, _helpers.isScrollAtEnd)(offsetBeforeScroll.value, layout.value.height, size.value.height, inverted);
|
|
132
|
+
|
|
133
|
+
// "never" at end: scroll along when keyboard closes to avoid jump
|
|
134
|
+
if (keyboardLiftBehavior === "never" && wasAtEnd && effective < padding.value) {
|
|
135
|
+
padding.value = effective;
|
|
136
|
+
if (scrollEffective === 0 && minimumPaddingAbsorbed > 0) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, 0, false);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (!(0, _helpers.shouldShiftContent)(keyboardLiftBehavior, wasAtEnd)) {
|
|
143
|
+
// Closing, not shifting: reduce padding to avoid gap
|
|
144
|
+
if (closing.value && effective < padding.value) {
|
|
145
|
+
padding.value = effective;
|
|
146
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// When blankSpace fully absorbs the keyboard, skip scroll
|
|
152
|
+
if (scrollEffective === 0 && minimumPaddingAbsorbed > 0) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Persistent: don't let shift decrease
|
|
157
|
+
if (keyboardLiftBehavior === "persistent") {
|
|
158
|
+
const currentShift = offsetBeforeScroll.value + padding.value - scroll.value;
|
|
159
|
+
if (effective < currentShift) {
|
|
160
|
+
// When at end, allow scrolling back (snap to end + reduce padding)
|
|
161
|
+
if (wasAtEnd) {
|
|
162
|
+
padding.value = effective;
|
|
163
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, 0, false);
|
|
164
|
+
} else if (closing.value) {
|
|
165
|
+
// Not at end: reduce padding to avoid gap
|
|
166
|
+
padding.value = effective;
|
|
167
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const target = offsetBeforeScroll.value + padding.value - scrollEffective;
|
|
173
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, target, false);
|
|
174
|
+
} else {
|
|
175
|
+
const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);
|
|
176
|
+
const minimumPaddingAbsorbed = (0, _helpers.getMinimumPaddingAbsorbed)(blankSpace.value, extraContentPadding.value) * minimumPaddingFractionOnOpen.value;
|
|
177
|
+
const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed);
|
|
178
|
+
const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);
|
|
179
|
+
|
|
180
|
+
// "never" closing: clamp scroll to valid range as inset shrinks
|
|
181
|
+
if (keyboardLiftBehavior === "never" && closing.value && effective < padding.value) {
|
|
182
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!(0, _helpers.shouldShiftContent)(keyboardLiftBehavior, true)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// "whenAtEnd" sentinel check (also used for blankSpace full absorption)
|
|
190
|
+
if (offsetBeforeScroll.value === -1) {
|
|
191
|
+
if (closing.value) {
|
|
192
|
+
// Keyboard didn't shift on open; ensure valid position on close
|
|
193
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// "persistent" closing: maintain position, clamped to valid range
|
|
199
|
+
if (keyboardLiftBehavior === "persistent" && closing.value) {
|
|
200
|
+
const keepAt = offsetBeforeScroll.value + padding.value;
|
|
201
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);
|
|
202
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, Math.min(keepAt, maxScroll), false);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const target = (0, _helpers.clampedScrollTarget)(offsetBeforeScroll.value, scrollEffective, size.value.height, layout.value.height, actualTotalPadding);
|
|
206
|
+
(0, _reactNativeReanimated.scrollTo)(scrollViewRef, 0, target, false);
|
|
207
|
+
|
|
208
|
+
// Track actual (clamped) displacement during open for symmetric close
|
|
209
|
+
if (!closing.value) {
|
|
210
|
+
actualOpenShift.value = target - offsetBeforeScroll.value;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
onEnd: e => {
|
|
215
|
+
"worklet";
|
|
216
|
+
|
|
217
|
+
if (freeze) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);
|
|
221
|
+
padding.value = effective;
|
|
222
|
+
|
|
223
|
+
// Record actual scroll displacement so close can be symmetric
|
|
224
|
+
if (effective > 0 && offsetBeforeScroll.value !== -1) {
|
|
225
|
+
actualOpenShift.value = scroll.value - offsetBeforeScroll.value;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}, [inverted, keyboardLiftBehavior, freeze, offset]);
|
|
229
|
+
return {
|
|
230
|
+
padding,
|
|
231
|
+
currentHeight,
|
|
232
|
+
contentOffsetY: undefined,
|
|
233
|
+
scroll,
|
|
234
|
+
layout,
|
|
235
|
+
size,
|
|
236
|
+
onLayout,
|
|
237
|
+
onContentSizeChange
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=index.js.map
|