react-native-keyboard-controller 1.7.0 → 1.9.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 +2 -0
- package/android/gradle.properties +1 -1
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +5 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +41 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +9 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +7 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputLayoutObserver.kt +93 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/{KeyboardAnimationCallback.kt → listeners/KeyboardAnimationCallback.kt} +86 -55
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +6 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +120 -50
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +5 -0
- package/ios/Extensions.swift +7 -0
- package/ios/KeyboardController.xcodeproj/project.pbxproj +41 -7
- package/ios/events/FocusedInputLayoutChangedEvent.h +16 -0
- package/ios/events/FocusedInputLayoutChangedEvent.m +75 -0
- package/ios/observers/FocusedInputLayoutObserver.swift +136 -0
- package/ios/{KeyboardControllerView.mm → views/KeyboardControllerView.mm} +54 -3
- package/ios/{KeyboardControllerViewManager.mm → views/KeyboardControllerViewManager.mm} +7 -0
- package/ios/{KeyboardControllerViewManager.swift → views/KeyboardControllerViewManager.swift} +25 -1
- package/jest/index.js +16 -0
- package/lib/commonjs/animated.js +34 -6
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/bindings.js.map +1 -1
- package/lib/commonjs/bindings.native.js.map +1 -1
- package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +9 -1
- package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
- package/lib/commonjs/components/KeyboardAvoidingView/index.js +7 -4
- package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardStickyView/index.js +44 -0
- package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -0
- package/lib/commonjs/components/index.js +7 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/context.js +9 -4
- package/lib/commonjs/context.js.map +1 -1
- package/lib/commonjs/hooks.js +16 -1
- package/lib/commonjs/hooks.js.map +1 -1
- package/lib/commonjs/index.js +8 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/internal.js.map +1 -1
- package/lib/commonjs/monkey-patch.android.js +32 -20
- package/lib/commonjs/monkey-patch.android.js.map +1 -1
- package/lib/commonjs/monkey-patch.js +11 -1
- package/lib/commonjs/monkey-patch.js.map +1 -1
- package/lib/commonjs/reanimated.js +5 -2
- package/lib/commonjs/reanimated.js.map +1 -1
- package/lib/commonjs/reanimated.native.js +18 -1
- package/lib/commonjs/reanimated.native.js.map +1 -1
- package/lib/commonjs/replicas.js +1 -1
- package/lib/commonjs/replicas.js.map +1 -1
- package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
- package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
- package/lib/commonjs/specs/NativeStatusBarManagerCompat.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/animated.js +37 -9
- package/lib/module/animated.js.map +1 -1
- package/lib/module/bindings.js.map +1 -1
- package/lib/module/bindings.native.js +1 -1
- package/lib/module/bindings.native.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/hooks.js +9 -1
- package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
- package/lib/module/components/KeyboardAvoidingView/index.js +9 -6
- package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
- package/lib/module/components/KeyboardStickyView/index.js +35 -0
- package/lib/module/components/KeyboardStickyView/index.js.map +1 -0
- package/lib/module/components/index.js +1 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/constants.js.map +1 -1
- package/lib/module/context.js +9 -4
- package/lib/module/context.js.map +1 -1
- package/lib/module/hooks.js +14 -1
- package/lib/module/hooks.js.map +1 -1
- package/lib/module/index.js +1 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal.js.map +1 -1
- package/lib/module/monkey-patch.android.js +26 -20
- package/lib/module/monkey-patch.android.js.map +1 -1
- package/lib/module/monkey-patch.js +3 -0
- package/lib/module/monkey-patch.js.map +1 -1
- package/lib/module/reanimated.js +3 -1
- package/lib/module/reanimated.js.map +1 -1
- package/lib/module/reanimated.native.js +16 -0
- package/lib/module/reanimated.native.js.map +1 -1
- package/lib/module/replicas.js +2 -2
- package/lib/module/replicas.js.map +1 -1
- package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
- package/lib/module/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
- package/lib/module/specs/NativeKeyboardController.js.map +1 -1
- package/lib/module/specs/NativeStatusBarManagerCompat.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/animated.d.ts +8 -1
- package/lib/typescript/components/KeyboardAvoidingView/hooks.d.ts +1 -0
- package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +2 -1
- package/lib/typescript/components/KeyboardStickyView/index.d.ts +20 -0
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/context.d.ts +6 -3
- package/lib/typescript/hooks.d.ts +9 -2
- package/lib/typescript/index.d.ts +1 -2
- package/lib/typescript/internal.d.ts +1 -1
- package/lib/typescript/monkey-patch.android.d.ts +2 -1
- package/lib/typescript/monkey-patch.d.ts +2 -0
- package/lib/typescript/reanimated.d.ts +2 -1
- package/lib/typescript/reanimated.native.d.ts +2 -1
- package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +13 -0
- package/lib/typescript/types.d.ts +17 -0
- package/package.json +9 -28
- package/src/animated.tsx +53 -9
- package/src/bindings.native.ts +1 -1
- package/src/bindings.ts +3 -1
- package/src/components/KeyboardAvoidingView/hooks.ts +9 -1
- package/src/components/KeyboardAvoidingView/index.tsx +15 -13
- package/src/components/KeyboardStickyView/index.tsx +63 -0
- package/src/components/index.ts +1 -0
- package/src/constants.ts +2 -0
- package/src/context.ts +16 -5
- package/src/hooks.ts +16 -6
- package/src/index.ts +1 -3
- package/src/internal.ts +3 -1
- package/src/monkey-patch.android.ts +27 -21
- package/src/monkey-patch.ts +3 -0
- package/src/reanimated.native.ts +30 -1
- package/src/reanimated.ts +13 -2
- package/src/replicas.ts +2 -2
- package/src/specs/KeyboardControllerViewNativeComponent.ts +16 -1
- package/src/specs/KeyboardGestureAreaNativeComponent.ts +2 -1
- package/src/specs/NativeKeyboardController.ts +2 -1
- package/src/specs/NativeStatusBarManagerCompat.ts +2 -1
- package/src/types.ts +28 -1
- /package/ios/{KeyboardMoveEvent.h → events/KeyboardMoveEvent.h} +0 -0
- /package/ios/{KeyboardMoveEvent.m → events/KeyboardMoveEvent.m} +0 -0
- /package/ios/{KeyboardMovementObserver.swift → observers/KeyboardMovementObserver.swift} +0 -0
- /package/ios/{KeyboardControllerView.h → views/KeyboardControllerView.h} +0 -0
package/src/animated.tsx
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import { Animated, Platform, StyleSheet
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Animated, Platform, StyleSheet } from 'react-native';
|
|
3
3
|
import Reanimated, { useSharedValue } from 'react-native-reanimated';
|
|
4
4
|
|
|
5
|
-
import { KeyboardContext } from './context';
|
|
6
|
-
import { useSharedHandlers, useAnimatedValue } from './internal';
|
|
7
5
|
import { KeyboardControllerView } from './bindings';
|
|
8
|
-
import {
|
|
6
|
+
import { KeyboardContext } from './context';
|
|
7
|
+
import { useAnimatedValue, useSharedHandlers } from './internal';
|
|
8
|
+
import { applyMonkeyPatch, revertMonkeyPatch } from './monkey-patch';
|
|
9
|
+
import {
|
|
10
|
+
useAnimatedKeyboardHandler,
|
|
11
|
+
useFocusedInputHandler,
|
|
12
|
+
} from './reanimated';
|
|
9
13
|
|
|
14
|
+
import type { KeyboardAnimationContext } from './context';
|
|
10
15
|
import type {
|
|
16
|
+
FocusedInputLayoutChangedEvent,
|
|
11
17
|
KeyboardControllerProps,
|
|
12
18
|
KeyboardHandler,
|
|
13
19
|
NativeEvent,
|
|
14
20
|
} from './types';
|
|
21
|
+
import type { ViewStyle } from 'react-native';
|
|
15
22
|
|
|
16
23
|
const KeyboardControllerViewAnimated = Reanimated.createAnimatedComponent(
|
|
17
24
|
Animated.createAnimatedComponent(
|
|
@@ -54,28 +61,41 @@ type KeyboardProviderProps = {
|
|
|
54
61
|
* @platform android
|
|
55
62
|
*/
|
|
56
63
|
navigationBarTranslucent?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* A boolean prop indicating whether the module is enabled. It indicate only initial state,
|
|
66
|
+
* i. e. if you try to change this prop after component mount it will not have any effect.
|
|
67
|
+
* To change the property in runtime use `useKeyboardController` hook and `setEnabled` method.
|
|
68
|
+
* Defaults to `true`.
|
|
69
|
+
*/
|
|
70
|
+
enabled?: boolean;
|
|
57
71
|
};
|
|
58
72
|
|
|
59
73
|
export const KeyboardProvider = ({
|
|
60
74
|
children,
|
|
61
75
|
statusBarTranslucent,
|
|
62
76
|
navigationBarTranslucent,
|
|
77
|
+
enabled: initiallyEnabled = true,
|
|
63
78
|
}: KeyboardProviderProps) => {
|
|
79
|
+
const [enabled, setEnabled] = useState(initiallyEnabled);
|
|
64
80
|
// animated values
|
|
65
81
|
const progress = useAnimatedValue(0);
|
|
66
82
|
const height = useAnimatedValue(0);
|
|
67
83
|
// shared values
|
|
68
84
|
const progressSV = useSharedValue(0);
|
|
69
85
|
const heightSV = useSharedValue(0);
|
|
86
|
+
const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
|
|
70
87
|
const { setHandlers, broadcast } = useSharedHandlers<KeyboardHandler>();
|
|
71
88
|
// memo
|
|
72
|
-
const context = useMemo(
|
|
89
|
+
const context = useMemo<KeyboardAnimationContext>(
|
|
73
90
|
() => ({
|
|
91
|
+
enabled,
|
|
74
92
|
animated: { progress: progress, height: Animated.multiply(height, -1) },
|
|
75
93
|
reanimated: { progress: progressSV, height: heightSV },
|
|
94
|
+
layout,
|
|
76
95
|
setHandlers,
|
|
96
|
+
setEnabled,
|
|
77
97
|
}),
|
|
78
|
-
[]
|
|
98
|
+
[enabled]
|
|
79
99
|
);
|
|
80
100
|
const style = useMemo(
|
|
81
101
|
() => [
|
|
@@ -108,7 +128,7 @@ export const KeyboardProvider = ({
|
|
|
108
128
|
heightSV.value = -event.height;
|
|
109
129
|
}
|
|
110
130
|
};
|
|
111
|
-
const
|
|
131
|
+
const keyboardHandler = useAnimatedKeyboardHandler(
|
|
112
132
|
{
|
|
113
133
|
onKeyboardMoveStart: (event: NativeEvent) => {
|
|
114
134
|
'worklet';
|
|
@@ -136,14 +156,38 @@ export const KeyboardProvider = ({
|
|
|
136
156
|
},
|
|
137
157
|
[]
|
|
138
158
|
);
|
|
159
|
+
const inputHandler = useFocusedInputHandler(
|
|
160
|
+
{
|
|
161
|
+
onFocusedInputLayoutChanged: (e) => {
|
|
162
|
+
'worklet';
|
|
163
|
+
|
|
164
|
+
if (e.target !== -1) {
|
|
165
|
+
layout.value = e;
|
|
166
|
+
} else {
|
|
167
|
+
layout.value = null;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
[]
|
|
172
|
+
);
|
|
173
|
+
// effects
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (enabled) {
|
|
176
|
+
applyMonkeyPatch();
|
|
177
|
+
} else {
|
|
178
|
+
revertMonkeyPatch();
|
|
179
|
+
}
|
|
180
|
+
}, [enabled]);
|
|
139
181
|
|
|
140
182
|
return (
|
|
141
183
|
<KeyboardContext.Provider value={context}>
|
|
142
184
|
<KeyboardControllerViewAnimated
|
|
143
|
-
|
|
185
|
+
enabled={enabled}
|
|
186
|
+
onKeyboardMoveReanimated={keyboardHandler}
|
|
144
187
|
onKeyboardMoveStart={Platform.OS === 'ios' ? onKeyboardMove : undefined}
|
|
145
188
|
onKeyboardMove={Platform.OS === 'android' ? onKeyboardMove : undefined}
|
|
146
189
|
onKeyboardMoveInteractive={onKeyboardMove}
|
|
190
|
+
onFocusedInputLayoutChangedReanimated={inputHandler}
|
|
147
191
|
navigationBarTranslucent={navigationBarTranslucent}
|
|
148
192
|
statusBarTranslucent={statusBarTranslucent}
|
|
149
193
|
// @ts-expect-error https://github.com/software-mansion/react-native-reanimated/pull/4923
|
package/src/bindings.native.ts
CHANGED
package/src/bindings.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
|
|
2
3
|
import type {
|
|
3
4
|
KeyboardControllerModule,
|
|
4
5
|
KeyboardControllerProps,
|
|
5
6
|
KeyboardEventsModule,
|
|
6
7
|
KeyboardGestureAreaProps,
|
|
7
8
|
} from './types';
|
|
9
|
+
import type { EmitterSubscription } from 'react-native';
|
|
8
10
|
|
|
9
11
|
const NOOP = () => {};
|
|
10
12
|
export const KeyboardController: KeyboardControllerModule = {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useSharedValue } from 'react-native-reanimated';
|
|
2
|
+
|
|
2
3
|
import { useKeyboardHandler } from '../../hooks';
|
|
3
4
|
|
|
4
5
|
export const useKeyboardAnimation = () => {
|
|
5
6
|
const heightWhenOpened = useSharedValue(0);
|
|
6
7
|
const height = useSharedValue(0);
|
|
7
8
|
const progress = useSharedValue(0);
|
|
9
|
+
const isClosed = useSharedValue(true);
|
|
8
10
|
|
|
9
11
|
useKeyboardHandler(
|
|
10
12
|
{
|
|
@@ -12,6 +14,7 @@ export const useKeyboardAnimation = () => {
|
|
|
12
14
|
'worklet';
|
|
13
15
|
|
|
14
16
|
if (e.height > 0) {
|
|
17
|
+
isClosed.value = false;
|
|
15
18
|
heightWhenOpened.value = e.height;
|
|
16
19
|
}
|
|
17
20
|
},
|
|
@@ -21,9 +24,14 @@ export const useKeyboardAnimation = () => {
|
|
|
21
24
|
progress.value = e.progress;
|
|
22
25
|
height.value = e.height;
|
|
23
26
|
},
|
|
27
|
+
onEnd: (e) => {
|
|
28
|
+
'worklet';
|
|
29
|
+
|
|
30
|
+
isClosed.value = e.height === 0;
|
|
31
|
+
},
|
|
24
32
|
},
|
|
25
33
|
[]
|
|
26
34
|
);
|
|
27
35
|
|
|
28
|
-
return { height, progress, heightWhenOpened };
|
|
36
|
+
return { height, progress, heightWhenOpened, isClosed };
|
|
29
37
|
};
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useMemo } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
LayoutRectangle,
|
|
4
|
-
useWindowDimensions,
|
|
5
|
-
View,
|
|
6
|
-
ViewProps,
|
|
7
|
-
} from 'react-native';
|
|
2
|
+
import { View, useWindowDimensions } from 'react-native';
|
|
8
3
|
import Reanimated, {
|
|
4
|
+
interpolate,
|
|
5
|
+
runOnUI,
|
|
9
6
|
useAnimatedStyle,
|
|
10
|
-
useWorkletCallback,
|
|
11
|
-
useSharedValue,
|
|
12
7
|
useDerivedValue,
|
|
13
|
-
|
|
8
|
+
useSharedValue,
|
|
9
|
+
useWorkletCallback,
|
|
14
10
|
} from 'react-native-reanimated';
|
|
11
|
+
|
|
15
12
|
import { useKeyboardAnimation } from './hooks';
|
|
16
13
|
|
|
14
|
+
import type { LayoutRectangle, ViewProps } from 'react-native';
|
|
15
|
+
|
|
17
16
|
type Props = {
|
|
18
17
|
/**
|
|
19
18
|
* Specify how to react to the presence of the keyboard.
|
|
@@ -76,11 +75,14 @@ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
|
|
|
76
75
|
return Math.max(frame.value.y + frame.value.height - keyboardY, 0);
|
|
77
76
|
}, [screenHeight, keyboardVerticalOffset]);
|
|
78
77
|
|
|
78
|
+
const onLayoutWorklet = useWorkletCallback((layout: LayoutRectangle) => {
|
|
79
|
+
if (keyboard.isClosed.value) {
|
|
80
|
+
initialFrame.value = layout;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
79
83
|
const onLayout = useCallback<NonNullable<ViewProps['onLayout']>>(
|
|
80
84
|
(e) => {
|
|
81
|
-
|
|
82
|
-
initialFrame.value = e.nativeEvent.layout;
|
|
83
|
-
}
|
|
85
|
+
runOnUI(onLayoutWorklet)(e.nativeEvent.layout);
|
|
84
86
|
onLayoutProps?.(e);
|
|
85
87
|
},
|
|
86
88
|
[onLayoutProps]
|
|
@@ -96,7 +98,7 @@ const KeyboardAvoidingView = forwardRef<View, React.PropsWithChildren<Props>>(
|
|
|
96
98
|
|
|
97
99
|
switch (behavior) {
|
|
98
100
|
case 'height':
|
|
99
|
-
if (
|
|
101
|
+
if (!keyboard.isClosed.value) {
|
|
100
102
|
return {
|
|
101
103
|
height: frame.value.height - bottomHeight,
|
|
102
104
|
flex: 0,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { forwardRef, useMemo } from 'react';
|
|
2
|
+
import Reanimated, {
|
|
3
|
+
interpolate,
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
} from 'react-native-reanimated';
|
|
6
|
+
|
|
7
|
+
import { useReanimatedKeyboardAnimation } from '../../hooks';
|
|
8
|
+
|
|
9
|
+
import type { View, ViewProps } from 'react-native';
|
|
10
|
+
|
|
11
|
+
type KeyboardStickyViewProps = {
|
|
12
|
+
/**
|
|
13
|
+
* Specify additional offset to the view for given keyboard state.
|
|
14
|
+
*/
|
|
15
|
+
offset?: {
|
|
16
|
+
/**
|
|
17
|
+
* Adds additional `translateY` when keyboard is close. By default `0`.
|
|
18
|
+
*/
|
|
19
|
+
closed?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Adds additional `translateY` when keyboard is open. By default `0`.
|
|
22
|
+
*/
|
|
23
|
+
opened?: number;
|
|
24
|
+
};
|
|
25
|
+
} & ViewProps;
|
|
26
|
+
|
|
27
|
+
const KeyboardStickyView = forwardRef<
|
|
28
|
+
View,
|
|
29
|
+
React.PropsWithChildren<KeyboardStickyViewProps>
|
|
30
|
+
>(
|
|
31
|
+
(
|
|
32
|
+
{ children, offset: { closed = 0, opened = 0 } = {}, style, ...props },
|
|
33
|
+
ref
|
|
34
|
+
) => {
|
|
35
|
+
const { height, progress } = useReanimatedKeyboardAnimation();
|
|
36
|
+
|
|
37
|
+
const stickyViewStyle = useAnimatedStyle(() => {
|
|
38
|
+
const offset = interpolate(progress.value, [0, 1], [closed, opened]);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
transform: [{ translateY: height.value + offset }],
|
|
42
|
+
};
|
|
43
|
+
}, [closed, opened]);
|
|
44
|
+
|
|
45
|
+
const styles = useMemo(
|
|
46
|
+
() => [style, stickyViewStyle],
|
|
47
|
+
[style, stickyViewStyle]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Reanimated.View
|
|
52
|
+
// @ts-expect-error because `ref` from reanimated is not compatible with react-native
|
|
53
|
+
ref={ref}
|
|
54
|
+
style={styles}
|
|
55
|
+
{...props}
|
|
56
|
+
>
|
|
57
|
+
{children}
|
|
58
|
+
</Reanimated.View>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export default KeyboardStickyView;
|
package/src/components/index.ts
CHANGED
package/src/constants.ts
CHANGED
|
@@ -12,6 +12,8 @@ export enum AndroidSoftInputModes {
|
|
|
12
12
|
SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5,
|
|
13
13
|
SOFT_INPUT_STATE_HIDDEN = 2,
|
|
14
14
|
SOFT_INPUT_STATE_UNCHANGED = 1,
|
|
15
|
+
// temporarily disable this rule to avoid breaking changes.
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
|
|
15
17
|
SOFT_INPUT_STATE_UNSPECIFIED = 0,
|
|
16
18
|
SOFT_INPUT_STATE_VISIBLE = 4,
|
|
17
19
|
}
|
package/src/context.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
import { Animated } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import type { FocusedInputLayoutChangedEvent, KeyboardHandlers } from './types';
|
|
5
|
+
import type React from 'react';
|
|
4
6
|
import type { SharedValue } from 'react-native-reanimated';
|
|
5
|
-
import type { KeyboardHandlers } from './types';
|
|
6
7
|
|
|
7
8
|
export type AnimatedContext = {
|
|
8
9
|
progress: Animated.Value;
|
|
@@ -13,18 +14,26 @@ export type ReanimatedContext = {
|
|
|
13
14
|
height: SharedValue<number>;
|
|
14
15
|
};
|
|
15
16
|
export type KeyboardAnimationContext = {
|
|
17
|
+
enabled: boolean;
|
|
16
18
|
animated: AnimatedContext;
|
|
17
19
|
reanimated: ReanimatedContext;
|
|
20
|
+
layout: SharedValue<FocusedInputLayoutChangedEvent | null>;
|
|
18
21
|
setHandlers: (handlers: KeyboardHandlers) => void;
|
|
22
|
+
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
|
|
19
23
|
};
|
|
20
24
|
const NOOP = () => {};
|
|
21
|
-
const
|
|
22
|
-
value
|
|
25
|
+
const withSharedValue = <T>(value: T): SharedValue<T> => ({
|
|
26
|
+
value,
|
|
23
27
|
addListener: NOOP,
|
|
24
28
|
removeListener: NOOP,
|
|
25
29
|
modify: NOOP,
|
|
26
|
-
};
|
|
30
|
+
});
|
|
31
|
+
const DEFAULT_SHARED_VALUE = withSharedValue(0);
|
|
32
|
+
const DEFAULT_LAYOUT = withSharedValue<FocusedInputLayoutChangedEvent | null>(
|
|
33
|
+
null
|
|
34
|
+
);
|
|
27
35
|
const defaultContext: KeyboardAnimationContext = {
|
|
36
|
+
enabled: true,
|
|
28
37
|
animated: {
|
|
29
38
|
progress: new Animated.Value(0),
|
|
30
39
|
height: new Animated.Value(0),
|
|
@@ -33,7 +42,9 @@ const defaultContext: KeyboardAnimationContext = {
|
|
|
33
42
|
progress: DEFAULT_SHARED_VALUE,
|
|
34
43
|
height: DEFAULT_SHARED_VALUE,
|
|
35
44
|
},
|
|
36
|
-
|
|
45
|
+
layout: DEFAULT_LAYOUT,
|
|
46
|
+
setHandlers: NOOP,
|
|
47
|
+
setEnabled: NOOP,
|
|
37
48
|
};
|
|
38
49
|
export const KeyboardContext = createContext(defaultContext);
|
|
39
50
|
export const useKeyboardContext = () => {
|
package/src/hooks.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
AnimatedContext,
|
|
5
|
-
ReanimatedContext,
|
|
6
|
-
useKeyboardContext,
|
|
7
|
-
} from './context';
|
|
8
3
|
import { KeyboardController } from './bindings';
|
|
9
4
|
import { AndroidSoftInputModes } from './constants';
|
|
5
|
+
import { useKeyboardContext } from './context';
|
|
10
6
|
import { uuid } from './utils';
|
|
11
7
|
|
|
8
|
+
import type { AnimatedContext, ReanimatedContext } from './context';
|
|
12
9
|
import type { KeyboardHandler } from './types';
|
|
10
|
+
import type { DependencyList } from 'react';
|
|
13
11
|
|
|
14
12
|
export const useResizeMode = () => {
|
|
15
13
|
useEffect(() => {
|
|
@@ -59,3 +57,15 @@ export function useKeyboardHandler(
|
|
|
59
57
|
useResizeMode();
|
|
60
58
|
useGenericKeyboardHandler(handler, deps);
|
|
61
59
|
}
|
|
60
|
+
|
|
61
|
+
export function useKeyboardController() {
|
|
62
|
+
const context = useKeyboardContext();
|
|
63
|
+
|
|
64
|
+
return { setEnabled: context.setEnabled, enabled: context.enabled };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function useReanimatedFocusedInput() {
|
|
68
|
+
const context = useKeyboardContext();
|
|
69
|
+
|
|
70
|
+
return { input: context.layout };
|
|
71
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import './monkey-patch';
|
|
2
|
-
|
|
3
1
|
export * from './bindings';
|
|
4
2
|
export * from './animated';
|
|
5
3
|
export * from './replicas';
|
|
@@ -8,4 +6,4 @@ export * from './hooks';
|
|
|
8
6
|
export * from './constants';
|
|
9
7
|
export * from './types';
|
|
10
8
|
|
|
11
|
-
export { KeyboardAvoidingView } from './components';
|
|
9
|
+
export { KeyboardAvoidingView, KeyboardStickyView } from './components';
|
package/src/internal.ts
CHANGED
|
@@ -15,7 +15,9 @@ import type { Handlers, NativeEvent } from './types';
|
|
|
15
15
|
* onEvent2: () => {},
|
|
16
16
|
* }
|
|
17
17
|
*/
|
|
18
|
-
export function useSharedHandlers<
|
|
18
|
+
export function useSharedHandlers<
|
|
19
|
+
T extends Record<string, (event: NativeEvent) => void>
|
|
20
|
+
>() {
|
|
19
21
|
const handlers = useSharedValue<Handlers<T>>({});
|
|
20
22
|
const jsHandlers = useRef<Handlers<T>>({});
|
|
21
23
|
|
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
// @ts-expect-error because there is no corresponding type definition
|
|
2
2
|
import * as NativeAndroidManager from 'react-native/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid';
|
|
3
3
|
|
|
4
|
+
const DefaultNativeAndroidManager = NativeAndroidManager.default;
|
|
4
5
|
const getConstants = NativeAndroidManager.default.getConstants;
|
|
6
|
+
const RCTStatusBarManagerCompat =
|
|
7
|
+
require('./specs/NativeStatusBarManagerCompat').default;
|
|
5
8
|
|
|
6
9
|
// On Android < 11 RN uses legacy API which breaks EdgeToEdge mode in RN, so
|
|
7
10
|
// in order to use library on all available platforms we have to monkey patch
|
|
8
11
|
// default RN implementation and use modern `WindowInsetsControllerCompat`.
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
12
|
+
export const applyMonkeyPatch = () => {
|
|
13
|
+
NativeAndroidManager.default = {
|
|
14
|
+
getConstants,
|
|
15
|
+
setColor(color: number, animated: boolean): void {
|
|
16
|
+
RCTStatusBarManagerCompat.setColor(color, animated);
|
|
17
|
+
},
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
setTranslucent(translucent: boolean): void {
|
|
20
|
+
RCTStatusBarManagerCompat.setTranslucent(translucent);
|
|
21
|
+
},
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
/**
|
|
24
|
+
* - statusBarStyles can be:
|
|
25
|
+
* - 'default'
|
|
26
|
+
* - 'dark-content'
|
|
27
|
+
*/
|
|
28
|
+
setStyle(statusBarStyle?: string): void {
|
|
29
|
+
RCTStatusBarManagerCompat.setStyle(statusBarStyle);
|
|
30
|
+
},
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
setHidden(hidden: boolean): void {
|
|
33
|
+
RCTStatusBarManagerCompat.setHidden(hidden);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export const revertMonkeyPatch = () => {
|
|
38
|
+
NativeAndroidManager.default = DefaultNativeAndroidManager;
|
|
33
39
|
};
|
package/src/monkey-patch.ts
CHANGED
package/src/reanimated.native.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { useEvent, useHandler } from 'react-native-reanimated';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
EventWithName,
|
|
5
|
+
FocusedInputHandlerHook,
|
|
6
|
+
FocusedInputLayoutChangedEvent,
|
|
7
|
+
KeyboardHandlerHook,
|
|
8
|
+
NativeEvent,
|
|
9
|
+
} from './types';
|
|
4
10
|
|
|
5
11
|
export const useAnimatedKeyboardHandler: KeyboardHandlerHook<
|
|
6
12
|
Record<string, unknown>,
|
|
@@ -49,3 +55,26 @@ export const useAnimatedKeyboardHandler: KeyboardHandlerHook<
|
|
|
49
55
|
doDependenciesDiffer
|
|
50
56
|
);
|
|
51
57
|
};
|
|
58
|
+
|
|
59
|
+
export const useFocusedInputHandler: FocusedInputHandlerHook<
|
|
60
|
+
Record<string, unknown>,
|
|
61
|
+
EventWithName<FocusedInputLayoutChangedEvent>
|
|
62
|
+
> = (handlers, dependencies) => {
|
|
63
|
+
const { context, doDependenciesDiffer } = useHandler(handlers, dependencies);
|
|
64
|
+
|
|
65
|
+
return useEvent(
|
|
66
|
+
(event) => {
|
|
67
|
+
'worklet';
|
|
68
|
+
const { onFocusedInputLayoutChanged } = handlers;
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
onFocusedInputLayoutChanged &&
|
|
72
|
+
event.eventName.endsWith('onFocusedInputLayoutChanged')
|
|
73
|
+
) {
|
|
74
|
+
onFocusedInputLayoutChanged(event, context);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
['onFocusedInputLayoutChanged'],
|
|
78
|
+
doDependenciesDiffer
|
|
79
|
+
);
|
|
80
|
+
};
|
package/src/reanimated.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
EventWithName,
|
|
3
|
+
FocusedInputHandlerHook,
|
|
4
|
+
FocusedInputLayoutChangedEvent,
|
|
5
|
+
KeyboardHandlerHook,
|
|
6
|
+
NativeEvent,
|
|
7
|
+
} from './types';
|
|
2
8
|
|
|
9
|
+
const NOOP = () => () => {};
|
|
3
10
|
export const useAnimatedKeyboardHandler: KeyboardHandlerHook<
|
|
4
11
|
Record<string, unknown>,
|
|
5
12
|
EventWithName<NativeEvent>
|
|
6
|
-
> =
|
|
13
|
+
> = NOOP;
|
|
14
|
+
export const useFocusedInputHandler: FocusedInputHandlerHook<
|
|
15
|
+
Record<string, unknown>,
|
|
16
|
+
EventWithName<FocusedInputLayoutChangedEvent>
|
|
17
|
+
> = NOOP;
|
package/src/replicas.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { Animated, Easing, Keyboard, Platform } from 'react-native';
|
|
3
3
|
import {
|
|
4
4
|
runOnUI,
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
useWorkletCallback,
|
|
9
9
|
withSpring,
|
|
10
10
|
} from 'react-native-reanimated';
|
|
11
|
-
import { useReanimatedKeyboardAnimation } from './hooks';
|
|
12
11
|
|
|
13
12
|
import { KeyboardController } from './bindings';
|
|
14
13
|
import { AndroidSoftInputModes } from './constants';
|
|
14
|
+
import { useReanimatedKeyboardAnimation } from './hooks';
|
|
15
15
|
|
|
16
16
|
const availableOSEventType = Platform.OS === 'ios' ? 'Will' : 'Did';
|
|
17
17
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
2
|
+
|
|
1
3
|
import type { HostComponent } from 'react-native';
|
|
2
4
|
import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes';
|
|
3
5
|
import type {
|
|
@@ -5,7 +7,6 @@ import type {
|
|
|
5
7
|
Double,
|
|
6
8
|
Int32,
|
|
7
9
|
} from 'react-native/Libraries/Types/CodegenTypes';
|
|
8
|
-
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
9
10
|
|
|
10
11
|
type KeyboardMoveEvent = Readonly<{
|
|
11
12
|
height: Double;
|
|
@@ -14,8 +15,21 @@ type KeyboardMoveEvent = Readonly<{
|
|
|
14
15
|
target: Int32;
|
|
15
16
|
}>;
|
|
16
17
|
|
|
18
|
+
type FocusedInputLayoutChangedEvent = Readonly<{
|
|
19
|
+
target: Int32;
|
|
20
|
+
layout: {
|
|
21
|
+
x: Double;
|
|
22
|
+
y: Double;
|
|
23
|
+
width: Double;
|
|
24
|
+
height: Double;
|
|
25
|
+
absoluteX: Double;
|
|
26
|
+
absoluteY: Double;
|
|
27
|
+
};
|
|
28
|
+
}>;
|
|
29
|
+
|
|
17
30
|
export interface NativeProps extends ViewProps {
|
|
18
31
|
// props
|
|
32
|
+
enabled?: boolean;
|
|
19
33
|
statusBarTranslucent?: boolean;
|
|
20
34
|
navigationBarTranslucent?: boolean;
|
|
21
35
|
// callbacks
|
|
@@ -23,6 +37,7 @@ export interface NativeProps extends ViewProps {
|
|
|
23
37
|
onKeyboardMove?: DirectEventHandler<KeyboardMoveEvent>;
|
|
24
38
|
onKeyboardMoveEnd?: DirectEventHandler<KeyboardMoveEvent>;
|
|
25
39
|
onKeyboardMoveInteractive?: DirectEventHandler<KeyboardMoveEvent>;
|
|
40
|
+
onFocusedInputLayoutChanged?: DirectEventHandler<FocusedInputLayoutChangedEvent>;
|
|
26
41
|
}
|
|
27
42
|
|
|
28
43
|
export default codegenNativeComponent<NativeProps>(
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
2
|
+
|
|
1
3
|
import type { HostComponent } from 'react-native';
|
|
2
4
|
import type { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes';
|
|
3
5
|
import type { WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
|
|
4
|
-
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
5
6
|
|
|
6
7
|
export interface NativeProps extends ViewProps {
|
|
7
8
|
interpolator?: WithDefault<'linear' | 'ios', 'linear'>;
|