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,143 @@
|
|
|
1
|
+
import { useSharedValue } from "react-native-reanimated";
|
|
2
|
+
import { useKeyboardHandler } from "../../../hooks";
|
|
3
|
+
import useScrollState from "../../hooks/useScrollState";
|
|
4
|
+
import { computeIOSContentOffset, getEffectiveHeight, getScrollEffective, getVisibleMinimumPaddingFraction, isScrollAtEnd, shouldShiftContent } from "./helpers";
|
|
5
|
+
/**
|
|
6
|
+
* Hook that manages keyboard-driven scrolling for chat-style scroll views.
|
|
7
|
+
* Calculates padding (extra scrollable space) and content shift values,
|
|
8
|
+
* using iOS-specific strategy (contentOffset set once in onStart).
|
|
9
|
+
*
|
|
10
|
+
* @param scrollViewRef - Animated ref to the scroll view.
|
|
11
|
+
* @param options - Configuration for inverted and keyboardLiftBehavior.
|
|
12
|
+
* @returns Shared values for padding and contentOffsetY.
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const { padding, contentOffsetY } = useChatKeyboard(ref, {
|
|
16
|
+
* inverted: false,
|
|
17
|
+
* keyboardLiftBehavior: "always",
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
function useChatKeyboard(scrollViewRef, options) {
|
|
22
|
+
const {
|
|
23
|
+
inverted,
|
|
24
|
+
keyboardLiftBehavior,
|
|
25
|
+
freeze,
|
|
26
|
+
offset,
|
|
27
|
+
blankSpace,
|
|
28
|
+
extraContentPadding
|
|
29
|
+
} = options;
|
|
30
|
+
const padding = useSharedValue(0);
|
|
31
|
+
const currentHeight = useSharedValue(0);
|
|
32
|
+
const contentOffsetY = useSharedValue(0);
|
|
33
|
+
const targetKeyboardHeight = useSharedValue(0);
|
|
34
|
+
const prevAbsorption = useSharedValue(0);
|
|
35
|
+
const {
|
|
36
|
+
layout,
|
|
37
|
+
size,
|
|
38
|
+
offset: scroll,
|
|
39
|
+
onLayout,
|
|
40
|
+
onContentSizeChange
|
|
41
|
+
} = useScrollState(scrollViewRef);
|
|
42
|
+
useKeyboardHandler({
|
|
43
|
+
onStart: e => {
|
|
44
|
+
"worklet";
|
|
45
|
+
|
|
46
|
+
if (freeze) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (e.height > 0) {
|
|
50
|
+
// eslint-disable-next-line react-compiler/react-compiler
|
|
51
|
+
targetKeyboardHeight.value = e.height;
|
|
52
|
+
}
|
|
53
|
+
const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);
|
|
54
|
+
const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted);
|
|
55
|
+
|
|
56
|
+
// Scale minimum padding absorption by how much of it is visible.
|
|
57
|
+
// Fully visible → full absorption; fully off-screen → no absorption.
|
|
58
|
+
const visibleFraction = getVisibleMinimumPaddingFraction(scroll.value, layout.value.height, size.value.height, blankSpace.value, inverted);
|
|
59
|
+
const visiblePadding = visibleFraction * blankSpace.value;
|
|
60
|
+
const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value);
|
|
61
|
+
const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed);
|
|
62
|
+
const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);
|
|
63
|
+
|
|
64
|
+
// persistent mode: when keyboard shrinks, clamp to valid range
|
|
65
|
+
if (keyboardLiftBehavior === "persistent" && effective < padding.value) {
|
|
66
|
+
padding.value = effective;
|
|
67
|
+
prevAbsorption.value = minimumPaddingAbsorbed;
|
|
68
|
+
if (inverted) {
|
|
69
|
+
const maxScroll = Math.max(size.value.height - layout.value.height, 0);
|
|
70
|
+
contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll));
|
|
71
|
+
} else {
|
|
72
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);
|
|
73
|
+
contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// never mode: when keyboard shrinks, clamp to valid range
|
|
79
|
+
// to avoid ghost padding
|
|
80
|
+
if (keyboardLiftBehavior === "never" && effective < padding.value && atEnd) {
|
|
81
|
+
padding.value = effective;
|
|
82
|
+
prevAbsorption.value = minimumPaddingAbsorbed;
|
|
83
|
+
if (inverted) {
|
|
84
|
+
const maxScroll = Math.max(size.value.height - layout.value.height, 0);
|
|
85
|
+
contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll));
|
|
86
|
+
} else {
|
|
87
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);
|
|
88
|
+
contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Undo only the scroll displacement that was actually applied
|
|
94
|
+
// (not the full padding, which includes the absorbed portion).
|
|
95
|
+
// Use the stored absorption from the previous event so that
|
|
96
|
+
// the unwind matches the shift that was originally applied.
|
|
97
|
+
const prevScrollEffective = getScrollEffective(padding.value, prevAbsorption.value);
|
|
98
|
+
const relativeScroll = inverted ? scroll.value + prevScrollEffective : scroll.value - prevScrollEffective;
|
|
99
|
+
padding.value = effective;
|
|
100
|
+
prevAbsorption.value = minimumPaddingAbsorbed;
|
|
101
|
+
if (!shouldShiftContent(keyboardLiftBehavior, atEnd)) {
|
|
102
|
+
// Preserve current scroll position so animated props
|
|
103
|
+
// don't re-apply a stale contentOffset when padding changes
|
|
104
|
+
contentOffsetY.value = scroll.value;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// When blankSpace fully absorbs the keyboard opening, preserve current scroll position
|
|
109
|
+
// (only when keyboard is open — effective > 0 — not when closing)
|
|
110
|
+
if (scrollEffective === 0 && minimumPaddingAbsorbed > 0 && effective > 0) {
|
|
111
|
+
contentOffsetY.value = scroll.value;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding);
|
|
115
|
+
},
|
|
116
|
+
onMove: () => {
|
|
117
|
+
"worklet";
|
|
118
|
+
|
|
119
|
+
// iOS doesn't need per-frame updates (contentOffset handles it)
|
|
120
|
+
},
|
|
121
|
+
onEnd: e => {
|
|
122
|
+
"worklet";
|
|
123
|
+
|
|
124
|
+
if (freeze) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);
|
|
128
|
+
padding.value = effective;
|
|
129
|
+
}
|
|
130
|
+
}, [inverted, keyboardLiftBehavior, freeze, offset, extraContentPadding]);
|
|
131
|
+
return {
|
|
132
|
+
padding,
|
|
133
|
+
currentHeight,
|
|
134
|
+
contentOffsetY,
|
|
135
|
+
scroll,
|
|
136
|
+
layout,
|
|
137
|
+
size,
|
|
138
|
+
onLayout,
|
|
139
|
+
onContentSizeChange
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export { useChatKeyboard };
|
|
143
|
+
//# sourceMappingURL=index.ios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useSharedValue","useKeyboardHandler","useScrollState","computeIOSContentOffset","getEffectiveHeight","getScrollEffective","getVisibleMinimumPaddingFraction","isScrollAtEnd","shouldShiftContent","useChatKeyboard","scrollViewRef","options","inverted","keyboardLiftBehavior","freeze","offset","blankSpace","extraContentPadding","padding","currentHeight","contentOffsetY","targetKeyboardHeight","prevAbsorption","layout","size","scroll","onLayout","onContentSizeChange","onStart","e","height","value","effective","atEnd","visibleFraction","visiblePadding","minimumPaddingAbsorbed","Math","max","scrollEffective","actualTotalPadding","maxScroll","min","prevScrollEffective","relativeScroll","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,234 @@
|
|
|
1
|
+
import { scrollTo, useSharedValue } from "react-native-reanimated";
|
|
2
|
+
import { useKeyboardHandler } from "../../../hooks";
|
|
3
|
+
import useScrollState from "../../hooks/useScrollState";
|
|
4
|
+
import { clampedScrollTarget, getEffectiveHeight, getMinimumPaddingAbsorbed, getScrollEffective, getVisibleMinimumPaddingFraction, isScrollAtEnd, shouldShiftContent } from "./helpers";
|
|
5
|
+
/**
|
|
6
|
+
* Hook that manages keyboard-driven scrolling for chat-style scroll views.
|
|
7
|
+
* Calculates padding (extra scrollable space) and content shift values,
|
|
8
|
+
* using per-frame scrollTo updates (Android and other platforms).
|
|
9
|
+
*
|
|
10
|
+
* @param scrollViewRef - Animated ref to the scroll view.
|
|
11
|
+
* @param options - Configuration for inverted and keyboardLiftBehavior.
|
|
12
|
+
* @returns Shared values for padding and contentOffsetY (always `undefined`).
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const { padding } = useChatKeyboard(ref, { inverted: false, keyboardLiftBehavior: "always" });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
function useChatKeyboard(scrollViewRef, options) {
|
|
19
|
+
const {
|
|
20
|
+
inverted,
|
|
21
|
+
keyboardLiftBehavior,
|
|
22
|
+
freeze,
|
|
23
|
+
offset,
|
|
24
|
+
blankSpace,
|
|
25
|
+
extraContentPadding
|
|
26
|
+
} = options;
|
|
27
|
+
const padding = useSharedValue(0);
|
|
28
|
+
const currentHeight = useSharedValue(0);
|
|
29
|
+
const offsetBeforeScroll = useSharedValue(0);
|
|
30
|
+
const targetKeyboardHeight = useSharedValue(0);
|
|
31
|
+
const closing = useSharedValue(false);
|
|
32
|
+
const minimumPaddingFractionOnOpen = useSharedValue(0);
|
|
33
|
+
const actualOpenShift = useSharedValue(0);
|
|
34
|
+
const {
|
|
35
|
+
layout,
|
|
36
|
+
size,
|
|
37
|
+
offset: scroll,
|
|
38
|
+
onLayout,
|
|
39
|
+
onContentSizeChange
|
|
40
|
+
} = useScrollState(scrollViewRef);
|
|
41
|
+
const clampScrollIfNeeded = (effective, totalPaddingForMaxScroll) => {
|
|
42
|
+
"worklet";
|
|
43
|
+
|
|
44
|
+
const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : effective;
|
|
45
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + paddingForMax, 0);
|
|
46
|
+
if (scroll.value > maxScroll) {
|
|
47
|
+
scrollTo(scrollViewRef, 0, maxScroll, false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
useKeyboardHandler({
|
|
51
|
+
onStart: e => {
|
|
52
|
+
"worklet";
|
|
53
|
+
|
|
54
|
+
if (freeze) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (e.height > 0) {
|
|
58
|
+
// eslint-disable-next-line react-compiler/react-compiler
|
|
59
|
+
targetKeyboardHeight.value = e.height;
|
|
60
|
+
closing.value = false;
|
|
61
|
+
} else {
|
|
62
|
+
closing.value = true;
|
|
63
|
+
}
|
|
64
|
+
const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);
|
|
65
|
+
const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted);
|
|
66
|
+
|
|
67
|
+
// Scale minimum padding absorption by how much of it is visible.
|
|
68
|
+
// Fully visible → full absorption; fully off-screen → no absorption.
|
|
69
|
+
const visibleFraction = getVisibleMinimumPaddingFraction(scroll.value, layout.value.height, size.value.height, blankSpace.value, inverted);
|
|
70
|
+
const minimumPaddingAbsorbed = visibleFraction >= 1 ? getMinimumPaddingAbsorbed(blankSpace.value, extraContentPadding.value) : 0;
|
|
71
|
+
const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed);
|
|
72
|
+
if (inverted && e.duration === -1) {
|
|
73
|
+
// Android inverted: skip post-interactive snap-back events
|
|
74
|
+
// (duration === -1 means the keyboard is re-establishing its
|
|
75
|
+
// position after an interactive gesture, not a real animation)
|
|
76
|
+
return;
|
|
77
|
+
} else if (e.height > 0) {
|
|
78
|
+
// Android: keyboard opening — set padding + capture scroll position
|
|
79
|
+
minimumPaddingFractionOnOpen.value = visibleFraction >= 1 ? 1 : 0;
|
|
80
|
+
padding.value = effective;
|
|
81
|
+
offsetBeforeScroll.value = scroll.value;
|
|
82
|
+
if (!inverted && keyboardLiftBehavior === "whenAtEnd" && !atEnd) {
|
|
83
|
+
// Sentinel: don't scroll in onMove (non-inverted only)
|
|
84
|
+
offsetBeforeScroll.value = -1;
|
|
85
|
+
} else if (!inverted && scrollEffective === 0) {
|
|
86
|
+
// blankSpace fully absorbs the keyboard — prevent scroll
|
|
87
|
+
offsetBeforeScroll.value = -1;
|
|
88
|
+
} else if (inverted && scrollEffective === 0) {
|
|
89
|
+
// blankSpace fully absorbs the keyboard — guard for inverted
|
|
90
|
+
offsetBeforeScroll.value = scroll.value;
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// Android: keyboard closing — re-capture scroll position
|
|
94
|
+
if (inverted) {
|
|
95
|
+
offsetBeforeScroll.value = scroll.value;
|
|
96
|
+
} else {
|
|
97
|
+
// Preserve "whenAtEnd" sentinel: if open didn't shift, close shouldn't either
|
|
98
|
+
if (offsetBeforeScroll.value !== -1) {
|
|
99
|
+
// Use the actual displacement recorded at end of open animation
|
|
100
|
+
// (not the theoretical value) so close is symmetric with open
|
|
101
|
+
offsetBeforeScroll.value = scroll.value - actualOpenShift.value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
onMove: e => {
|
|
107
|
+
"worklet";
|
|
108
|
+
|
|
109
|
+
if (freeze) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
currentHeight.value = e.height;
|
|
113
|
+
if (inverted) {
|
|
114
|
+
// Skip post-interactive snap-back (duration === -1)
|
|
115
|
+
if (e.duration === -1) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);
|
|
119
|
+
const minimumPaddingAbsorbed = getMinimumPaddingAbsorbed(blankSpace.value, extraContentPadding.value) * minimumPaddingFractionOnOpen.value;
|
|
120
|
+
const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed);
|
|
121
|
+
const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);
|
|
122
|
+
|
|
123
|
+
// Check if we should shift content based on position when keyboard started
|
|
124
|
+
const wasAtEnd = isScrollAtEnd(offsetBeforeScroll.value, layout.value.height, size.value.height, inverted);
|
|
125
|
+
|
|
126
|
+
// "never" at end: scroll along when keyboard closes to avoid jump
|
|
127
|
+
if (keyboardLiftBehavior === "never" && wasAtEnd && effective < padding.value) {
|
|
128
|
+
padding.value = effective;
|
|
129
|
+
if (scrollEffective === 0 && minimumPaddingAbsorbed > 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
scrollTo(scrollViewRef, 0, 0, false);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!shouldShiftContent(keyboardLiftBehavior, wasAtEnd)) {
|
|
136
|
+
// Closing, not shifting: reduce padding to avoid gap
|
|
137
|
+
if (closing.value && effective < padding.value) {
|
|
138
|
+
padding.value = effective;
|
|
139
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// When blankSpace fully absorbs the keyboard, skip scroll
|
|
145
|
+
if (scrollEffective === 0 && minimumPaddingAbsorbed > 0) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Persistent: don't let shift decrease
|
|
150
|
+
if (keyboardLiftBehavior === "persistent") {
|
|
151
|
+
const currentShift = offsetBeforeScroll.value + padding.value - scroll.value;
|
|
152
|
+
if (effective < currentShift) {
|
|
153
|
+
// When at end, allow scrolling back (snap to end + reduce padding)
|
|
154
|
+
if (wasAtEnd) {
|
|
155
|
+
padding.value = effective;
|
|
156
|
+
scrollTo(scrollViewRef, 0, 0, false);
|
|
157
|
+
} else if (closing.value) {
|
|
158
|
+
// Not at end: reduce padding to avoid gap
|
|
159
|
+
padding.value = effective;
|
|
160
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const target = offsetBeforeScroll.value + padding.value - scrollEffective;
|
|
166
|
+
scrollTo(scrollViewRef, 0, target, false);
|
|
167
|
+
} else {
|
|
168
|
+
const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);
|
|
169
|
+
const minimumPaddingAbsorbed = getMinimumPaddingAbsorbed(blankSpace.value, extraContentPadding.value) * minimumPaddingFractionOnOpen.value;
|
|
170
|
+
const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed);
|
|
171
|
+
const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);
|
|
172
|
+
|
|
173
|
+
// "never" closing: clamp scroll to valid range as inset shrinks
|
|
174
|
+
if (keyboardLiftBehavior === "never" && closing.value && effective < padding.value) {
|
|
175
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (!shouldShiftContent(keyboardLiftBehavior, true)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// "whenAtEnd" sentinel check (also used for blankSpace full absorption)
|
|
183
|
+
if (offsetBeforeScroll.value === -1) {
|
|
184
|
+
if (closing.value) {
|
|
185
|
+
// Keyboard didn't shift on open; ensure valid position on close
|
|
186
|
+
clampScrollIfNeeded(effective, actualTotalPadding);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// "persistent" closing: maintain position, clamped to valid range
|
|
192
|
+
if (keyboardLiftBehavior === "persistent" && closing.value) {
|
|
193
|
+
const keepAt = offsetBeforeScroll.value + padding.value;
|
|
194
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);
|
|
195
|
+
scrollTo(scrollViewRef, 0, Math.min(keepAt, maxScroll), false);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const target = clampedScrollTarget(offsetBeforeScroll.value, scrollEffective, size.value.height, layout.value.height, actualTotalPadding);
|
|
199
|
+
scrollTo(scrollViewRef, 0, target, false);
|
|
200
|
+
|
|
201
|
+
// Track actual (clamped) displacement during open for symmetric close
|
|
202
|
+
if (!closing.value) {
|
|
203
|
+
actualOpenShift.value = target - offsetBeforeScroll.value;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
onEnd: e => {
|
|
208
|
+
"worklet";
|
|
209
|
+
|
|
210
|
+
if (freeze) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);
|
|
214
|
+
padding.value = effective;
|
|
215
|
+
|
|
216
|
+
// Record actual scroll displacement so close can be symmetric
|
|
217
|
+
if (effective > 0 && offsetBeforeScroll.value !== -1) {
|
|
218
|
+
actualOpenShift.value = scroll.value - offsetBeforeScroll.value;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}, [inverted, keyboardLiftBehavior, freeze, offset]);
|
|
222
|
+
return {
|
|
223
|
+
padding,
|
|
224
|
+
currentHeight,
|
|
225
|
+
contentOffsetY: undefined,
|
|
226
|
+
scroll,
|
|
227
|
+
layout,
|
|
228
|
+
size,
|
|
229
|
+
onLayout,
|
|
230
|
+
onContentSizeChange
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
export { useChatKeyboard };
|
|
234
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["scrollTo","useSharedValue","useKeyboardHandler","useScrollState","clampedScrollTarget","getEffectiveHeight","getMinimumPaddingAbsorbed","getScrollEffective","getVisibleMinimumPaddingFraction","isScrollAtEnd","shouldShiftContent","useChatKeyboard","scrollViewRef","options","inverted","keyboardLiftBehavior","freeze","offset","blankSpace","extraContentPadding","padding","currentHeight","offsetBeforeScroll","targetKeyboardHeight","closing","minimumPaddingFractionOnOpen","actualOpenShift","layout","size","scroll","onLayout","onContentSizeChange","clampScrollIfNeeded","effective","totalPaddingForMaxScroll","paddingForMax","undefined","maxScroll","Math","max","value","height","onStart","e","atEnd","visibleFraction","minimumPaddingAbsorbed","scrollEffective","duration","onMove","actualTotalPadding","wasAtEnd","currentShift","target","keepAt","min","onEnd","contentOffsetY"],"sources":["index.ts"],"sourcesContent":["import { scrollTo, useSharedValue } from \"react-native-reanimated\";\n\nimport { useKeyboardHandler } from \"../../../hooks\";\nimport useScrollState from \"../../hooks/useScrollState\";\n\nimport {\n clampedScrollTarget,\n getEffectiveHeight,\n getMinimumPaddingAbsorbed,\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 per-frame scrollTo updates (Android and other platforms).\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 (always `undefined`).\n * @example\n * ```tsx\n * const { padding } = useChatKeyboard(ref, { inverted: false, keyboardLiftBehavior: \"always\" });\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 offsetBeforeScroll = useSharedValue(0);\n const targetKeyboardHeight = useSharedValue(0);\n const closing = useSharedValue(false);\n const minimumPaddingFractionOnOpen = useSharedValue(0);\n const actualOpenShift = useSharedValue(0);\n const {\n layout,\n size,\n offset: scroll,\n onLayout,\n onContentSizeChange,\n } = useScrollState(scrollViewRef);\n const clampScrollIfNeeded = (\n effective: number,\n totalPaddingForMaxScroll?: number,\n ) => {\n \"worklet\";\n\n const paddingForMax =\n totalPaddingForMaxScroll !== undefined\n ? totalPaddingForMaxScroll\n : effective;\n const maxScroll = Math.max(\n size.value.height - layout.value.height + paddingForMax,\n 0,\n );\n\n if (scroll.value > maxScroll) {\n scrollTo(scrollViewRef, 0, maxScroll, false);\n }\n };\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 closing.value = false;\n } else {\n closing.value = true;\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 minimumPaddingAbsorbed =\n visibleFraction >= 1\n ? getMinimumPaddingAbsorbed(\n blankSpace.value,\n extraContentPadding.value,\n )\n : 0;\n const scrollEffective = getScrollEffective(\n effective,\n minimumPaddingAbsorbed,\n );\n\n if (inverted && e.duration === -1) {\n // Android inverted: skip post-interactive snap-back events\n // (duration === -1 means the keyboard is re-establishing its\n // position after an interactive gesture, not a real animation)\n return;\n } else if (e.height > 0) {\n // Android: keyboard opening — set padding + capture scroll position\n minimumPaddingFractionOnOpen.value = visibleFraction >= 1 ? 1 : 0;\n padding.value = effective;\n offsetBeforeScroll.value = scroll.value;\n\n if (!inverted && keyboardLiftBehavior === \"whenAtEnd\" && !atEnd) {\n // Sentinel: don't scroll in onMove (non-inverted only)\n offsetBeforeScroll.value = -1;\n } else if (!inverted && scrollEffective === 0) {\n // blankSpace fully absorbs the keyboard — prevent scroll\n offsetBeforeScroll.value = -1;\n } else if (inverted && scrollEffective === 0) {\n // blankSpace fully absorbs the keyboard — guard for inverted\n offsetBeforeScroll.value = scroll.value;\n }\n } else {\n // Android: keyboard closing — re-capture scroll position\n if (inverted) {\n offsetBeforeScroll.value = scroll.value;\n } else {\n // Preserve \"whenAtEnd\" sentinel: if open didn't shift, close shouldn't either\n if (offsetBeforeScroll.value !== -1) {\n // Use the actual displacement recorded at end of open animation\n // (not the theoretical value) so close is symmetric with open\n offsetBeforeScroll.value = scroll.value - actualOpenShift.value;\n }\n }\n }\n },\n onMove: (e) => {\n \"worklet\";\n\n if (freeze) {\n return;\n }\n\n currentHeight.value = e.height;\n\n if (inverted) {\n // Skip post-interactive snap-back (duration === -1)\n if (e.duration === -1) {\n return;\n }\n\n const effective = getEffectiveHeight(\n e.height,\n targetKeyboardHeight.value,\n offset,\n );\n\n const minimumPaddingAbsorbed =\n getMinimumPaddingAbsorbed(\n blankSpace.value,\n extraContentPadding.value,\n ) * minimumPaddingFractionOnOpen.value;\n const scrollEffective = getScrollEffective(\n effective,\n minimumPaddingAbsorbed,\n );\n const actualTotalPadding = Math.max(\n blankSpace.value,\n effective + extraContentPadding.value,\n );\n\n // Check if we should shift content based on position when keyboard started\n const wasAtEnd = isScrollAtEnd(\n offsetBeforeScroll.value,\n layout.value.height,\n size.value.height,\n inverted,\n );\n\n // \"never\" at end: scroll along when keyboard closes to avoid jump\n if (\n keyboardLiftBehavior === \"never\" &&\n wasAtEnd &&\n effective < padding.value\n ) {\n padding.value = effective;\n\n if (scrollEffective === 0 && minimumPaddingAbsorbed > 0) {\n return;\n }\n\n scrollTo(scrollViewRef, 0, 0, false);\n\n return;\n }\n\n if (!shouldShiftContent(keyboardLiftBehavior, wasAtEnd)) {\n // Closing, not shifting: reduce padding to avoid gap\n if (closing.value && effective < padding.value) {\n padding.value = effective;\n clampScrollIfNeeded(effective, actualTotalPadding);\n }\n\n return;\n }\n\n // When blankSpace fully absorbs the keyboard, skip scroll\n if (scrollEffective === 0 && minimumPaddingAbsorbed > 0) {\n return;\n }\n\n // Persistent: don't let shift decrease\n if (keyboardLiftBehavior === \"persistent\") {\n const currentShift =\n offsetBeforeScroll.value + padding.value - scroll.value;\n\n if (effective < currentShift) {\n // When at end, allow scrolling back (snap to end + reduce padding)\n if (wasAtEnd) {\n padding.value = effective;\n scrollTo(scrollViewRef, 0, 0, false);\n } else if (closing.value) {\n // Not at end: reduce padding to avoid gap\n padding.value = effective;\n clampScrollIfNeeded(effective, actualTotalPadding);\n }\n\n return;\n }\n }\n\n const target =\n offsetBeforeScroll.value + padding.value - scrollEffective;\n\n scrollTo(scrollViewRef, 0, target, false);\n } else {\n const effective = getEffectiveHeight(\n e.height,\n targetKeyboardHeight.value,\n offset,\n );\n\n const minimumPaddingAbsorbed =\n getMinimumPaddingAbsorbed(\n blankSpace.value,\n extraContentPadding.value,\n ) * minimumPaddingFractionOnOpen.value;\n const scrollEffective = getScrollEffective(\n effective,\n minimumPaddingAbsorbed,\n );\n const actualTotalPadding = Math.max(\n blankSpace.value,\n effective + extraContentPadding.value,\n );\n\n // \"never\" closing: clamp scroll to valid range as inset shrinks\n if (\n keyboardLiftBehavior === \"never\" &&\n closing.value &&\n effective < padding.value\n ) {\n clampScrollIfNeeded(effective, actualTotalPadding);\n\n return;\n }\n\n if (!shouldShiftContent(keyboardLiftBehavior, true)) {\n return;\n }\n\n // \"whenAtEnd\" sentinel check (also used for blankSpace full absorption)\n if (offsetBeforeScroll.value === -1) {\n if (closing.value) {\n // Keyboard didn't shift on open; ensure valid position on close\n clampScrollIfNeeded(effective, actualTotalPadding);\n }\n\n return;\n }\n\n // \"persistent\" closing: maintain position, clamped to valid range\n if (keyboardLiftBehavior === \"persistent\" && closing.value) {\n const keepAt = offsetBeforeScroll.value + padding.value;\n const maxScroll = Math.max(\n size.value.height - layout.value.height + actualTotalPadding,\n 0,\n );\n\n scrollTo(scrollViewRef, 0, Math.min(keepAt, maxScroll), false);\n\n return;\n }\n\n const target = clampedScrollTarget(\n offsetBeforeScroll.value,\n scrollEffective,\n size.value.height,\n layout.value.height,\n actualTotalPadding,\n );\n\n scrollTo(scrollViewRef, 0, target, false);\n\n // Track actual (clamped) displacement during open for symmetric close\n if (!closing.value) {\n actualOpenShift.value = target - offsetBeforeScroll.value;\n }\n }\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 // Record actual scroll displacement so close can be symmetric\n if (effective > 0 && offsetBeforeScroll.value !== -1) {\n actualOpenShift.value = scroll.value - offsetBeforeScroll.value;\n }\n },\n },\n [inverted, keyboardLiftBehavior, freeze, offset],\n );\n\n return {\n padding,\n currentHeight,\n contentOffsetY: undefined,\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 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { LayoutChangeEvent } from \"react-native\";\nimport type { SharedValue } from \"react-native-reanimated\";\n\ntype KeyboardLiftBehavior = \"always\" | \"whenAtEnd\" | \"persistent\" | \"never\";\n\ntype UseChatKeyboardOptions = {\n inverted: boolean;\n keyboardLiftBehavior: KeyboardLiftBehavior;\n freeze: boolean;\n offset: number;\n blankSpace: SharedValue<number>;\n /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */\n extraContentPadding: SharedValue<number>;\n};\n\ntype UseChatKeyboardReturn = {\n /** Extra scrollable space (= keyboard height). Used as contentInset on iOS, contentInsetBottom/contentInsetTop on Android. */\n padding: SharedValue<number>;\n /** Raw keyboard height updated every frame in onMove. Used to force Reanimated commits on Fabric. */\n currentHeight: SharedValue<number>;\n /** Absolute Y content offset for iOS (set once in onStart). `undefined` on Android. */\n contentOffsetY: SharedValue<number> | undefined;\n /** Current vertical scroll offset. */\n scroll: SharedValue<number>;\n /** Visible viewport dimensions. */\n layout: SharedValue<{ width: number; height: number }>;\n /** Total content dimensions. */\n size: SharedValue<{ width: number; height: number }>;\n /** Callback to attach to ScrollView's onLayout prop to capture initial viewport dimensions. */\n onLayout: (e: LayoutChangeEvent) => void;\n /** Callback to attach to ScrollView's onContentSizeChange prop to capture initial content dimensions. */\n onContentSizeChange: (w: number, h: number) => void;\n};\n\nexport type {\n KeyboardLiftBehavior,\n UseChatKeyboardOptions,\n UseChatKeyboardReturn,\n};\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { scrollTo, useAnimatedReaction } from "react-native-reanimated";
|
|
2
|
+
import { isScrollAtEnd, shouldShiftContent } from "../useChatKeyboard/helpers";
|
|
3
|
+
/**
|
|
4
|
+
* Hook that reacts to `extraContentPadding` changes and conditionally
|
|
5
|
+
* adjusts the scroll position using `scrollTo` on both iOS and Android.
|
|
6
|
+
*
|
|
7
|
+
* Padding extension (scrollable range) is handled externally via a
|
|
8
|
+
* `useDerivedValue` that sums keyboard padding + extra content padding.
|
|
9
|
+
* This hook only handles the scroll correction.
|
|
10
|
+
*
|
|
11
|
+
* @param options - Configuration and shared values.
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* useExtraContentPadding({ scrollViewRef, extraContentPadding, ... });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function useExtraContentPadding(options) {
|
|
18
|
+
const {
|
|
19
|
+
scrollViewRef,
|
|
20
|
+
extraContentPadding,
|
|
21
|
+
keyboardPadding,
|
|
22
|
+
blankSpace,
|
|
23
|
+
scroll,
|
|
24
|
+
layout,
|
|
25
|
+
size,
|
|
26
|
+
inverted,
|
|
27
|
+
keyboardLiftBehavior,
|
|
28
|
+
freeze
|
|
29
|
+
} = options;
|
|
30
|
+
useAnimatedReaction(() => extraContentPadding.value, (current, previous) => {
|
|
31
|
+
if (freeze || previous === null) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const rawDelta = current - previous;
|
|
35
|
+
if (rawDelta === 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Compute effective delta considering blankSpace floor
|
|
40
|
+
const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous);
|
|
41
|
+
const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current);
|
|
42
|
+
const effectiveDelta = currentTotal - previousTotal;
|
|
43
|
+
if (effectiveDelta === 0) {
|
|
44
|
+
// blankSpace absorbed the change
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted);
|
|
48
|
+
|
|
49
|
+
// "persistent": scroll on grow, hold position on shrink (unless at end)
|
|
50
|
+
if (keyboardLiftBehavior === "persistent" && effectiveDelta < 0 && !atEnd) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!shouldShiftContent(keyboardLiftBehavior, atEnd)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (inverted) {
|
|
57
|
+
const target = Math.max(scroll.value - effectiveDelta, -currentTotal);
|
|
58
|
+
scrollTo(scrollViewRef, 0, target, false);
|
|
59
|
+
} else {
|
|
60
|
+
const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0);
|
|
61
|
+
const target = Math.min(scroll.value + effectiveDelta, maxScroll);
|
|
62
|
+
scrollTo(scrollViewRef, 0, target, false);
|
|
63
|
+
}
|
|
64
|
+
}, [inverted, keyboardLiftBehavior, freeze]);
|
|
65
|
+
}
|
|
66
|
+
export { useExtraContentPadding };
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["scrollTo","useAnimatedReaction","isScrollAtEnd","shouldShiftContent","useExtraContentPadding","options","scrollViewRef","extraContentPadding","keyboardPadding","blankSpace","scroll","layout","size","inverted","keyboardLiftBehavior","freeze","value","current","previous","rawDelta","previousTotal","Math","max","currentTotal","effectiveDelta","atEnd","height","target","maxScroll","min"],"sources":["index.ts"],"sourcesContent":["import { scrollTo, useAnimatedReaction } from \"react-native-reanimated\";\n\nimport { isScrollAtEnd, shouldShiftContent } from \"../useChatKeyboard/helpers\";\n\nimport type { KeyboardLiftBehavior } from \"../useChatKeyboard/types\";\nimport type { AnimatedRef, SharedValue } from \"react-native-reanimated\";\nimport type Reanimated from \"react-native-reanimated\";\n\ntype UseExtraContentPaddingOptions = {\n scrollViewRef: AnimatedRef<Reanimated.ScrollView>;\n extraContentPadding: SharedValue<number>;\n /** Keyboard-only padding from useChatKeyboard — used to compute total padding for clamping. */\n keyboardPadding: SharedValue<number>;\n /** Minimum inset floor — used to absorb keyboard and extraContentPadding changes. */\n blankSpace: SharedValue<number>;\n /** Current vertical scroll offset. */\n scroll: SharedValue<number>;\n /** Visible viewport dimensions. */\n layout: SharedValue<{ width: number; height: number }>;\n /** Total content dimensions. */\n size: SharedValue<{ width: number; height: number }>;\n inverted: boolean;\n keyboardLiftBehavior: KeyboardLiftBehavior;\n freeze: boolean;\n};\n\n/**\n * Hook that reacts to `extraContentPadding` changes and conditionally\n * adjusts the scroll position using `scrollTo` on both iOS and Android.\n *\n * Padding extension (scrollable range) is handled externally via a\n * `useDerivedValue` that sums keyboard padding + extra content padding.\n * This hook only handles the scroll correction.\n *\n * @param options - Configuration and shared values.\n * @example\n * ```tsx\n * useExtraContentPadding({ scrollViewRef, extraContentPadding, ... });\n * ```\n */\nfunction useExtraContentPadding(options: UseExtraContentPaddingOptions): void {\n const {\n scrollViewRef,\n extraContentPadding,\n keyboardPadding,\n blankSpace,\n scroll,\n layout,\n size,\n inverted,\n keyboardLiftBehavior,\n freeze,\n } = options;\n\n useAnimatedReaction(\n () => extraContentPadding.value,\n (current, previous) => {\n if (freeze || previous === null) {\n return;\n }\n\n const rawDelta = current - previous;\n\n if (rawDelta === 0) {\n return;\n }\n\n // Compute effective delta considering blankSpace floor\n const previousTotal = Math.max(\n blankSpace.value,\n keyboardPadding.value + previous,\n );\n const currentTotal = Math.max(\n blankSpace.value,\n keyboardPadding.value + current,\n );\n const effectiveDelta = currentTotal - previousTotal;\n\n if (effectiveDelta === 0) {\n // blankSpace absorbed the change\n return;\n }\n\n const atEnd = isScrollAtEnd(\n scroll.value,\n layout.value.height,\n size.value.height,\n inverted,\n );\n\n // \"persistent\": scroll on grow, hold position on shrink (unless at end)\n if (\n keyboardLiftBehavior === \"persistent\" &&\n effectiveDelta < 0 &&\n !atEnd\n ) {\n return;\n }\n\n if (!shouldShiftContent(keyboardLiftBehavior, atEnd)) {\n return;\n }\n\n if (inverted) {\n const target = Math.max(scroll.value - effectiveDelta, -currentTotal);\n\n scrollTo(scrollViewRef, 0, target, false);\n } else {\n const maxScroll = Math.max(\n size.value.height - layout.value.height + currentTotal,\n 0,\n );\n const target = Math.min(scroll.value + effectiveDelta, maxScroll);\n\n scrollTo(scrollViewRef, 0, target, false);\n }\n },\n [inverted, keyboardLiftBehavior, freeze],\n );\n}\n\nexport { useExtraContentPadding };\nexport type { UseExtraContentPaddingOptions };\n"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,mBAAmB,QAAQ,yBAAyB;AAEvE,SAASC,aAAa,EAAEC,kBAAkB,QAAQ,4BAA4B;AAwB9E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,sBAAsBA,CAACC,OAAsC,EAAQ;EAC5E,MAAM;IACJC,aAAa;IACbC,mBAAmB;IACnBC,eAAe;IACfC,UAAU;IACVC,MAAM;IACNC,MAAM;IACNC,IAAI;IACJC,QAAQ;IACRC,oBAAoB;IACpBC;EACF,CAAC,GAAGV,OAAO;EAEXJ,mBAAmB,CACjB,MAAMM,mBAAmB,CAACS,KAAK,EAC/B,CAACC,OAAO,EAAEC,QAAQ,KAAK;IACrB,IAAIH,MAAM,IAAIG,QAAQ,KAAK,IAAI,EAAE;MAC/B;IACF;IAEA,MAAMC,QAAQ,GAAGF,OAAO,GAAGC,QAAQ;IAEnC,IAAIC,QAAQ,KAAK,CAAC,EAAE;MAClB;IACF;;IAEA;IACA,MAAMC,aAAa,GAAGC,IAAI,CAACC,GAAG,CAC5Bb,UAAU,CAACO,KAAK,EAChBR,eAAe,CAACQ,KAAK,GAAGE,QAC1B,CAAC;IACD,MAAMK,YAAY,GAAGF,IAAI,CAACC,GAAG,CAC3Bb,UAAU,CAACO,KAAK,EAChBR,eAAe,CAACQ,KAAK,GAAGC,OAC1B,CAAC;IACD,MAAMO,cAAc,GAAGD,YAAY,GAAGH,aAAa;IAEnD,IAAII,cAAc,KAAK,CAAC,EAAE;MACxB;MACA;IACF;IAEA,MAAMC,KAAK,GAAGvB,aAAa,CACzBQ,MAAM,CAACM,KAAK,EACZL,MAAM,CAACK,KAAK,CAACU,MAAM,EACnBd,IAAI,CAACI,KAAK,CAACU,MAAM,EACjBb,QACF,CAAC;;IAED;IACA,IACEC,oBAAoB,KAAK,YAAY,IACrCU,cAAc,GAAG,CAAC,IAClB,CAACC,KAAK,EACN;MACA;IACF;IAEA,IAAI,CAACtB,kBAAkB,CAACW,oBAAoB,EAAEW,KAAK,CAAC,EAAE;MACpD;IACF;IAEA,IAAIZ,QAAQ,EAAE;MACZ,MAAMc,MAAM,GAAGN,IAAI,CAACC,GAAG,CAACZ,MAAM,CAACM,KAAK,GAAGQ,cAAc,EAAE,CAACD,YAAY,CAAC;MAErEvB,QAAQ,CAACM,aAAa,EAAE,CAAC,EAAEqB,MAAM,EAAE,KAAK,CAAC;IAC3C,CAAC,MAAM;MACL,MAAMC,SAAS,GAAGP,IAAI,CAACC,GAAG,CACxBV,IAAI,CAACI,KAAK,CAACU,MAAM,GAAGf,MAAM,CAACK,KAAK,CAACU,MAAM,GAAGH,YAAY,EACtD,CACF,CAAC;MACD,MAAMI,MAAM,GAAGN,IAAI,CAACQ,GAAG,CAACnB,MAAM,CAACM,KAAK,GAAGQ,cAAc,EAAEI,SAAS,CAAC;MAEjE5B,QAAQ,CAACM,aAAa,EAAE,CAAC,EAAEqB,MAAM,EAAE,KAAK,CAAC;IAC3C;EACF,CAAC,EACD,CAACd,QAAQ,EAAEC,oBAAoB,EAAEC,MAAM,CACzC,CAAC;AACH;AAEA,SAASX,sBAAsB","ignoreList":[]}
|
|
@@ -34,9 +34,11 @@ const KeyboardStickyView = /*#__PURE__*/forwardRef(({
|
|
|
34
34
|
outputRange: [closed, opened]
|
|
35
35
|
});
|
|
36
36
|
const styles = useMemo(() => {
|
|
37
|
+
const disabled = Animated.add(Animated.multiply(height, 0), closed);
|
|
38
|
+
const active = Animated.add(height, offset);
|
|
37
39
|
return [{
|
|
38
40
|
transform: [{
|
|
39
|
-
translateY: enabled ?
|
|
41
|
+
translateY: enabled ? active : disabled
|
|
40
42
|
}]
|
|
41
43
|
}, style];
|
|
42
44
|
}, [closed, enabled, height, offset, style]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","forwardRef","useMemo","Animated","useKeyboardAnimation","KeyboardStickyView","children","offset","closed","opened","style","enabled","props","ref","height","progress","interpolate","inputRange","outputRange","styles","
|
|
1
|
+
{"version":3,"names":["React","forwardRef","useMemo","Animated","useKeyboardAnimation","KeyboardStickyView","children","offset","closed","opened","style","enabled","props","ref","height","progress","interpolate","inputRange","outputRange","styles","disabled","add","multiply","active","transform","translateY","createElement","View","_extends"],"sources":["index.tsx"],"sourcesContent":["import React, { forwardRef, useMemo } from \"react\";\nimport { Animated } from \"react-native\";\n\nimport { useKeyboardAnimation } from \"../../hooks\";\n\nimport type { View, ViewProps } from \"react-native\";\n\nexport type KeyboardStickyViewProps = {\n /**\n * Specify additional offset to the view for given keyboard state.\n */\n offset?: {\n /**\n * Adds additional `translateY` when keyboard is close. By default `0`.\n */\n closed?: number;\n /**\n * Adds additional `translateY` when keyboard is open. By default `0`.\n */\n opened?: number;\n };\n\n /** Controls whether this `KeyboardStickyView` instance should take effect. Default is `true`. */\n enabled?: boolean;\n} & ViewProps;\n\n/**\n * A View component that sticks to the keyboard and moves with it when it appears or disappears.\n * The view can be configured with custom offsets for both closed and open keyboard states.\n *\n * @returns An animated View component that sticks to the keyboard.\n * @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-sticky-view|Documentation} page for more details.\n * @example\n * ```tsx\n * <KeyboardStickyView offset={{ closed: 0, opened: 20 }}>\n * <Button title=\"Submit\" />\n * </KeyboardStickyView>\n * ```\n */\nconst KeyboardStickyView = forwardRef<\n View,\n React.PropsWithChildren<KeyboardStickyViewProps>\n>(\n (\n {\n children,\n offset: { closed = 0, opened = 0 } = {},\n style,\n enabled = true,\n ...props\n },\n ref,\n ) => {\n const { height, progress } = useKeyboardAnimation();\n\n const offset = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [closed, opened],\n });\n\n const styles = useMemo(() => {\n const disabled = Animated.add(Animated.multiply(height, 0), closed);\n const active = Animated.add(height, offset);\n\n return [\n {\n transform: [{ translateY: enabled ? active : disabled }],\n },\n style,\n ];\n }, [closed, enabled, height, offset, style]);\n\n return (\n <Animated.View ref={ref} style={styles} {...props}>\n {children}\n </Animated.View>\n );\n },\n);\n\nexport default KeyboardStickyView;\n"],"mappings":";AAAA,OAAOA,KAAK,IAAIC,UAAU,EAAEC,OAAO,QAAQ,OAAO;AAClD,SAASC,QAAQ,QAAQ,cAAc;AAEvC,SAASC,oBAAoB,QAAQ,aAAa;AAuBlD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAkB,gBAAGJ,UAAU,CAInC,CACE;EACEK,QAAQ;EACRC,MAAM,EAAE;IAAEC,MAAM,GAAG,CAAC;IAAEC,MAAM,GAAG;EAAE,CAAC,GAAG,CAAC,CAAC;EACvCC,KAAK;EACLC,OAAO,GAAG,IAAI;EACd,GAAGC;AACL,CAAC,EACDC,GAAG,KACA;EACH,MAAM;IAAEC,MAAM;IAAEC;EAAS,CAAC,GAAGX,oBAAoB,CAAC,CAAC;EAEnD,MAAMG,MAAM,GAAGQ,QAAQ,CAACC,WAAW,CAAC;IAClCC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClBC,WAAW,EAAE,CAACV,MAAM,EAAEC,MAAM;EAC9B,CAAC,CAAC;EAEF,MAAMU,MAAM,GAAGjB,OAAO,CAAC,MAAM;IAC3B,MAAMkB,QAAQ,GAAGjB,QAAQ,CAACkB,GAAG,CAAClB,QAAQ,CAACmB,QAAQ,CAACR,MAAM,EAAE,CAAC,CAAC,EAAEN,MAAM,CAAC;IACnE,MAAMe,MAAM,GAAGpB,QAAQ,CAACkB,GAAG,CAACP,MAAM,EAAEP,MAAM,CAAC;IAE3C,OAAO,CACL;MACEiB,SAAS,EAAE,CAAC;QAAEC,UAAU,EAAEd,OAAO,GAAGY,MAAM,GAAGH;MAAS,CAAC;IACzD,CAAC,EACDV,KAAK,CACN;EACH,CAAC,EAAE,CAACF,MAAM,EAAEG,OAAO,EAAEG,MAAM,EAAEP,MAAM,EAAEG,KAAK,CAAC,CAAC;EAE5C,oBACEV,KAAA,CAAA0B,aAAA,CAACvB,QAAQ,CAACwB,IAAI,EAAAC,QAAA;IAACf,GAAG,EAAEA,GAAI;IAACH,KAAK,EAAES;EAAO,GAAKP,KAAK,GAC9CN,QACY,CAAC;AAEpB,CACF,CAAC;AAED,eAAeD,kBAAkB","ignoreList":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
2
2
|
import React, { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { StyleSheet, View } from "react-native";
|
|
4
|
-
import { FocusedInputEvents } from "../../bindings";
|
|
4
|
+
import { FocusedInputEvents, RCTKeyboardToolbarGroupView } from "../../bindings";
|
|
5
5
|
import { useKeyboardState } from "../../hooks";
|
|
6
6
|
import KeyboardStickyView from "../KeyboardStickyView";
|
|
7
7
|
import Arrow from "./Arrow";
|
|
@@ -168,6 +168,7 @@ KeyboardToolbar.Content = Content;
|
|
|
168
168
|
KeyboardToolbar.Prev = Prev;
|
|
169
169
|
KeyboardToolbar.Next = Next;
|
|
170
170
|
KeyboardToolbar.Done = Done;
|
|
171
|
+
KeyboardToolbar.Group = RCTKeyboardToolbarGroupView;
|
|
171
172
|
export { colors as DefaultKeyboardToolbarTheme };
|
|
172
173
|
export default KeyboardToolbar;
|
|
173
174
|
//# sourceMappingURL=index.js.map
|