react-native-keyboard-controller 1.17.5 → 1.18.1
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 +4 -1
- package/android/src/base/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +3 -2
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardBackgroundViewManager.kt +19 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +4 -0
- package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +3 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/Context.kt +20 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +2 -12
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardBackgroundViewManagerImpl.kt +13 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +2 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +4 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/{StatusBarManagerCompatModuleImpl.kt → statusbar/StatusBarManagerCompatModuleImpl.kt} +31 -5
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/statusbar/StatusBarModuleProxy.kt +86 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +9 -11
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/background/KeyboardBackgroundViewGroup.kt +35 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/views/background/Skins.kt +91 -0
- package/android/src/main/jni/reactnativekeyboardcontroller.h +1 -0
- package/android/src/main/res/values/attrs.xml +7 -0
- package/android/src/main/res/values/colors.xml +35 -0
- package/android/src/main/res/values-v29/colors.xml +5 -0
- package/android/src/main/res/values-v30/colors.xml +17 -0
- package/android/src/main/res/values-v33/colors.xml +16 -0
- package/android/src/main/res/values-v34/colors.xml +18 -0
- package/android/src/main/res/values-v35/colors.xml +10 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardBackgroundViewManager.kt +15 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +5 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +3 -1
- package/android/src/turbo/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +2 -2
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewComponentDescriptor.h +27 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewShadowNode.cpp +14 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewShadowNode.h +30 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewState.h +28 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderComponentDescriptor.h +27 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderShadowNode.cpp +14 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderShadowNode.h +30 -0
- package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderState.h +28 -0
- package/ios/KeyboardControllerModule.mm +9 -0
- package/ios/delegates/KCTextInputCompositeDelegate.swift +5 -5
- package/ios/events/KeyboardEventEmitterPayload.swift +1 -1
- package/ios/extensions/UIResponder.swift +27 -0
- package/ios/interactive/KeyboardAreaExtender.swift +5 -5
- package/ios/observers/FocusedInputObserver.swift +14 -5
- package/ios/observers/KeyboardMovementObserver.swift +7 -1
- package/ios/protocols/TextInput.swift +22 -0
- package/ios/swizzling/UIResponderSwizzle.swift +3 -0
- package/ios/views/KeyboardBackgroundViewManager.h +29 -0
- package/ios/views/KeyboardBackgroundViewManager.mm +175 -0
- package/ios/views/KeyboardExtenderContainerView.swift +59 -0
- package/ios/views/KeyboardExtenderManager.h +29 -0
- package/ios/views/KeyboardExtenderManager.mm +287 -0
- package/jest/index.js +6 -1
- package/lib/commonjs/animated.js +7 -10
- package/lib/commonjs/animated.js.map +1 -1
- package/lib/commonjs/bindings.js +15 -1
- package/lib/commonjs/bindings.js.map +1 -1
- package/lib/commonjs/bindings.native.js +5 -1
- package/lib/commonjs/bindings.native.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +49 -29
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/Arrow.js +2 -3
- package/lib/commonjs/components/KeyboardToolbar/Arrow.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/Button.js +2 -3
- package/lib/commonjs/components/KeyboardToolbar/Button.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/index.js +2 -2
- package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/commonjs/hooks/useKeyboardState/index.js +18 -8
- package/lib/commonjs/hooks/useKeyboardState/index.js.map +1 -1
- package/lib/commonjs/index.js +8 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/module.js +2 -1
- package/lib/commonjs/module.js.map +1 -1
- package/lib/commonjs/specs/KeyboardBackgroundViewNativeComponent.js +12 -0
- package/lib/commonjs/specs/KeyboardBackgroundViewNativeComponent.js.map +1 -0
- package/lib/commonjs/specs/KeyboardExtenderNativeComponent.js +13 -0
- package/lib/commonjs/specs/KeyboardExtenderNativeComponent.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/commonjs/views/KeyboardExtender/index.ios.js +43 -0
- package/lib/commonjs/views/KeyboardExtender/index.ios.js.map +1 -0
- package/lib/commonjs/views/KeyboardExtender/index.js +46 -0
- package/lib/commonjs/views/KeyboardExtender/index.js.map +1 -0
- package/lib/commonjs/views/index.js +7 -0
- package/lib/commonjs/views/index.js.map +1 -1
- package/lib/module/animated.js +8 -11
- package/lib/module/animated.js.map +1 -1
- package/lib/module/bindings.js +14 -0
- package/lib/module/bindings.js.map +1 -1
- package/lib/module/bindings.native.js +4 -0
- package/lib/module/bindings.native.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +50 -30
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/Arrow.js +2 -2
- package/lib/module/components/KeyboardToolbar/Arrow.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/Button.js +2 -2
- package/lib/module/components/KeyboardToolbar/Button.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/index.js +2 -2
- package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
- package/lib/module/hooks/useKeyboardState/index.js +18 -6
- package/lib/module/hooks/useKeyboardState/index.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/module.js +2 -1
- package/lib/module/module.js.map +1 -1
- package/lib/module/specs/KeyboardBackgroundViewNativeComponent.js +5 -0
- package/lib/module/specs/KeyboardBackgroundViewNativeComponent.js.map +1 -0
- package/lib/module/specs/KeyboardExtenderNativeComponent.js +6 -0
- package/lib/module/specs/KeyboardExtenderNativeComponent.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/module/views/KeyboardExtender/index.ios.js +36 -0
- package/lib/module/views/KeyboardExtender/index.ios.js.map +1 -0
- package/lib/module/views/KeyboardExtender/index.js +39 -0
- package/lib/module/views/KeyboardExtender/index.js.map +1 -0
- package/lib/module/views/index.js +1 -0
- package/lib/module/views/index.js.map +1 -1
- package/lib/typescript/animated.d.ts +7 -0
- package/lib/typescript/bindings.d.ts +14 -1
- package/lib/typescript/bindings.native.d.ts +3 -1
- package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +2 -2
- package/lib/typescript/hooks/useKeyboardState/index.d.ts +8 -2
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/specs/KeyboardBackgroundViewNativeComponent.d.ts +6 -0
- package/lib/typescript/specs/KeyboardExtenderNativeComponent.d.ts +7 -0
- package/lib/typescript/specs/NativeKeyboardController.d.ts +1 -0
- package/lib/typescript/types/module.d.ts +15 -3
- package/lib/typescript/types/views.d.ts +5 -0
- package/lib/typescript/views/KeyboardExtender/index.d.ts +19 -0
- package/lib/typescript/views/KeyboardExtender/index.ios.d.ts +19 -0
- package/lib/typescript/views/index.d.ts +1 -0
- package/package.json +7 -3
- package/react-native.config.js +1 -0
- package/src/animated.tsx +14 -9
- package/src/bindings.native.ts +8 -0
- package/src/bindings.ts +18 -0
- package/src/components/KeyboardAwareScrollView/index.tsx +72 -47
- package/src/components/KeyboardToolbar/Arrow.tsx +2 -2
- package/src/components/KeyboardToolbar/Button.tsx +2 -2
- package/src/components/KeyboardToolbar/index.tsx +2 -2
- package/src/hooks/useKeyboardState/index.ts +25 -6
- package/src/index.ts +1 -1
- package/src/module.ts +2 -1
- package/src/specs/KeyboardBackgroundViewNativeComponent.ts +10 -0
- package/src/specs/KeyboardExtenderNativeComponent.ts +13 -0
- package/src/specs/NativeKeyboardController.ts +1 -0
- package/src/types/module.ts +17 -3
- package/src/types/views.ts +5 -0
- package/src/views/KeyboardExtender/index.ios.tsx +35 -0
- package/src/views/KeyboardExtender/index.tsx +42 -0
- package/src/views/index.ts +1 -0
- package/ios/extensions/UIKeyboardAppearance.swift +0 -21
- package/lib/commonjs/components/hooks/useColorScheme.js +0 -10
- package/lib/commonjs/components/hooks/useColorScheme.js.map +0 -1
- package/lib/commonjs/monkey-patch.android.js +0 -53
- package/lib/commonjs/monkey-patch.android.js.map +0 -1
- package/lib/commonjs/monkey-patch.js +0 -11
- package/lib/commonjs/monkey-patch.js.map +0 -1
- package/lib/module/components/hooks/useColorScheme.js +0 -4
- package/lib/module/components/hooks/useColorScheme.js.map +0 -1
- package/lib/module/monkey-patch.android.js +0 -42
- package/lib/module/monkey-patch.android.js.map +0 -1
- package/lib/module/monkey-patch.js +0 -5
- package/lib/module/monkey-patch.js.map +0 -1
- package/lib/typescript/components/hooks/useColorScheme.d.ts +0 -2
- package/lib/typescript/monkey-patch.android.d.ts +0 -2
- package/lib/typescript/monkey-patch.d.ts +0 -2
- package/src/components/hooks/useColorScheme.ts +0 -5
- package/src/monkey-patch.android.ts +0 -41
- package/src/monkey-patch.ts +0 -5
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { KeyboardExtenderProps } from "../../types";
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
/**
|
|
5
|
+
* A component that embeds its children into the keyboard thus enhancing keyboard functionality.
|
|
6
|
+
*
|
|
7
|
+
* @param props - Component props.
|
|
8
|
+
* @returns A view component that renders inside the keyboard above all system buttons.
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <KeyboardExtender>
|
|
12
|
+
* <Button>10$</Button>
|
|
13
|
+
* <Button>20$</Button>
|
|
14
|
+
* <Button>50$</Button>
|
|
15
|
+
* </KeyboardExtender>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare const KeyboardExtender: (props: PropsWithChildren<KeyboardExtenderProps>) => React.JSX.Element;
|
|
19
|
+
export default KeyboardExtender;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-keyboard-controller",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.1",
|
|
4
4
|
"description": "Keyboard manager which works in identical way on both iOS and Android",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -60,11 +60,13 @@
|
|
|
60
60
|
"avoid keyboard",
|
|
61
61
|
"sticky view",
|
|
62
62
|
"over keyboard view",
|
|
63
|
+
"keyboard background view",
|
|
63
64
|
"keyboard aware scroll view",
|
|
64
65
|
"keyboard toolbar",
|
|
65
66
|
"keyboard done button",
|
|
66
67
|
"keyboard next button",
|
|
67
68
|
"keyboard previous button",
|
|
69
|
+
"extend keyboard",
|
|
68
70
|
"ios",
|
|
69
71
|
"android"
|
|
70
72
|
],
|
|
@@ -79,7 +81,7 @@
|
|
|
79
81
|
"registry": "https://registry.npmjs.org/"
|
|
80
82
|
},
|
|
81
83
|
"dependencies": {
|
|
82
|
-
"react-native-is-edge-to-edge": "^1.1
|
|
84
|
+
"react-native-is-edge-to-edge": "^1.2.1"
|
|
83
85
|
},
|
|
84
86
|
"devDependencies": {
|
|
85
87
|
"@commitlint/config-conventional": "^11.0.0",
|
|
@@ -180,7 +182,9 @@
|
|
|
180
182
|
"componentProvider": {
|
|
181
183
|
"KeyboardControllerView": "KeyboardControllerView",
|
|
182
184
|
"KeyboardGestureArea": "KeyboardGestureArea",
|
|
183
|
-
"OverKeyboardView": "OverKeyboardView"
|
|
185
|
+
"OverKeyboardView": "OverKeyboardView",
|
|
186
|
+
"KeyboardBackgroundView": "KeyboardBackgroundView",
|
|
187
|
+
"KeyboardExtender": "KeyboardExtender"
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
}
|
package/react-native.config.js
CHANGED
|
@@ -6,6 +6,7 @@ module.exports = {
|
|
|
6
6
|
"KeyboardControllerViewComponentDescriptor",
|
|
7
7
|
"KeyboardGestureAreaComponentDescriptor",
|
|
8
8
|
"OverKeyboardViewComponentDescriptor",
|
|
9
|
+
"KeyboardBackgroundViewComponentDescriptor",
|
|
9
10
|
],
|
|
10
11
|
cmakeListsPath: "../android/src/main/jni/CMakeLists.txt",
|
|
11
12
|
},
|
package/src/animated.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint react/jsx-sort-props: off */
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Animated, Platform, StyleSheet } from "react-native";
|
|
4
4
|
import {
|
|
5
5
|
controlEdgeToEdgeValues,
|
|
@@ -11,7 +11,7 @@ import { KeyboardControllerView } from "./bindings";
|
|
|
11
11
|
import { KeyboardContext } from "./context";
|
|
12
12
|
import { focusedInputEventsMap, keyboardEventsMap } from "./event-mappings";
|
|
13
13
|
import { useAnimatedValue, useEventHandlerRegistration } from "./internal";
|
|
14
|
-
import {
|
|
14
|
+
import { KeyboardController } from "./module";
|
|
15
15
|
import {
|
|
16
16
|
useAnimatedKeyboardHandler,
|
|
17
17
|
useFocusedInputLayoutHandler,
|
|
@@ -83,6 +83,13 @@ type KeyboardProviderProps = {
|
|
|
83
83
|
* Defaults to `true`.
|
|
84
84
|
*/
|
|
85
85
|
enabled?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* A boolean prop indicating whether to preload the keyboard to reduce time-to-interaction (TTI) on first input focus.
|
|
88
|
+
* Defaults to `true`.
|
|
89
|
+
*
|
|
90
|
+
* @platform ios
|
|
91
|
+
*/
|
|
92
|
+
preload?: boolean;
|
|
86
93
|
};
|
|
87
94
|
|
|
88
95
|
// capture `Platform.OS` in separate variable to avoid deep workletization of entire RN package
|
|
@@ -110,6 +117,7 @@ export const KeyboardProvider = (props: KeyboardProviderProps) => {
|
|
|
110
117
|
navigationBarTranslucent,
|
|
111
118
|
preserveEdgeToEdge,
|
|
112
119
|
enabled: initiallyEnabled = true,
|
|
120
|
+
preload = true,
|
|
113
121
|
} = props;
|
|
114
122
|
// ref
|
|
115
123
|
const viewTagRef = useRef<React.Component<KeyboardControllerProps>>(null);
|
|
@@ -216,14 +224,11 @@ export const KeyboardProvider = (props: KeyboardProviderProps) => {
|
|
|
216
224
|
[],
|
|
217
225
|
);
|
|
218
226
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
applyMonkeyPatch();
|
|
223
|
-
} else {
|
|
224
|
-
revertMonkeyPatch();
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
if (preload) {
|
|
229
|
+
KeyboardController.preload();
|
|
225
230
|
}
|
|
226
|
-
}, [
|
|
231
|
+
}, [preload]);
|
|
227
232
|
|
|
228
233
|
if (__DEV__) {
|
|
229
234
|
controlEdgeToEdgeValues({
|
package/src/bindings.native.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { NativeEventEmitter, Platform } from "react-native";
|
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
FocusedInputEventsModule,
|
|
5
|
+
KeyboardBackgroundViewProps,
|
|
5
6
|
KeyboardControllerNativeModule,
|
|
6
7
|
KeyboardControllerProps,
|
|
7
8
|
KeyboardEventsModule,
|
|
9
|
+
KeyboardExtenderProps,
|
|
8
10
|
KeyboardGestureAreaProps,
|
|
9
11
|
OverKeyboardViewProps,
|
|
10
12
|
WindowDimensionsEventsModule,
|
|
@@ -60,3 +62,9 @@ export const KeyboardGestureArea: React.FC<KeyboardGestureAreaProps> =
|
|
|
60
62
|
: ({ children }: KeyboardGestureAreaProps) => children;
|
|
61
63
|
export const RCTOverKeyboardView: React.FC<OverKeyboardViewProps> =
|
|
62
64
|
require("./specs/OverKeyboardViewNativeComponent").default;
|
|
65
|
+
export const KeyboardBackgroundView: React.FC<KeyboardBackgroundViewProps> =
|
|
66
|
+
require("./specs/KeyboardBackgroundViewNativeComponent").default;
|
|
67
|
+
export const RCTKeyboardExtender: React.FC<KeyboardExtenderProps> =
|
|
68
|
+
Platform.OS === "ios"
|
|
69
|
+
? require("./specs/KeyboardExtenderNativeComponent").default
|
|
70
|
+
: ({ children }: KeyboardExtenderProps) => children;
|
package/src/bindings.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { View } from "react-native";
|
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
FocusedInputEventsModule,
|
|
5
|
+
KeyboardBackgroundViewProps,
|
|
5
6
|
KeyboardControllerNativeModule,
|
|
6
7
|
KeyboardControllerProps,
|
|
7
8
|
KeyboardEventsModule,
|
|
9
|
+
KeyboardExtenderProps,
|
|
8
10
|
KeyboardGestureAreaProps,
|
|
9
11
|
OverKeyboardViewProps,
|
|
10
12
|
WindowDimensionsEventsModule,
|
|
@@ -16,6 +18,7 @@ const NOOP = () => {};
|
|
|
16
18
|
export const KeyboardControllerNative: KeyboardControllerNativeModule = {
|
|
17
19
|
setDefaultMode: NOOP,
|
|
18
20
|
setInputMode: NOOP,
|
|
21
|
+
preload: NOOP,
|
|
19
22
|
dismiss: NOOP,
|
|
20
23
|
setFocusTo: NOOP,
|
|
21
24
|
addListener: NOOP,
|
|
@@ -59,3 +62,18 @@ export const KeyboardGestureArea =
|
|
|
59
62
|
View as unknown as React.FC<KeyboardGestureAreaProps>;
|
|
60
63
|
export const RCTOverKeyboardView =
|
|
61
64
|
View as unknown as React.FC<OverKeyboardViewProps>;
|
|
65
|
+
/**
|
|
66
|
+
* A view that matches keyboard background.
|
|
67
|
+
*
|
|
68
|
+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-background-view|Documentation} page for more details.
|
|
69
|
+
*/
|
|
70
|
+
export const KeyboardBackgroundView =
|
|
71
|
+
View as unknown as React.FC<KeyboardBackgroundViewProps>;
|
|
72
|
+
/**
|
|
73
|
+
* A container that will embed its children into the keyboard
|
|
74
|
+
* and will always show them above the keyboard.
|
|
75
|
+
*
|
|
76
|
+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-extender|Documentation} page for more details.
|
|
77
|
+
*/
|
|
78
|
+
export const RCTKeyboardExtender =
|
|
79
|
+
View as unknown as React.FC<KeyboardExtenderProps>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef, useCallback, useEffect, useMemo } from "react";
|
|
2
2
|
import Reanimated, {
|
|
3
|
+
clamp,
|
|
3
4
|
interpolate,
|
|
4
5
|
runOnUI,
|
|
5
6
|
scrollTo,
|
|
@@ -32,7 +33,7 @@ import type {
|
|
|
32
33
|
} from "react-native-keyboard-controller";
|
|
33
34
|
|
|
34
35
|
export type KeyboardAwareScrollViewProps = {
|
|
35
|
-
/** The distance between keyboard and focused `TextInput` when keyboard is shown. Default is `0`. */
|
|
36
|
+
/** The distance between the keyboard and the caret inside a focused `TextInput` when the keyboard is shown. Default is `0`. */
|
|
36
37
|
bottomOffset?: number;
|
|
37
38
|
/** Prevents automatic scrolling of the `ScrollView` when the keyboard gets hidden, maintaining the current screen position. Default is `false`. */
|
|
38
39
|
disableScrollOnKeyboardHide?: boolean;
|
|
@@ -127,6 +128,8 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
127
128
|
const scrollBeforeKeyboardMovement = useSharedValue(0);
|
|
128
129
|
const { input } = useReanimatedFocusedInput();
|
|
129
130
|
const layout = useSharedValue<FocusedInputLayoutChangedEvent | null>(null);
|
|
131
|
+
const lastSelection =
|
|
132
|
+
useSharedValue<FocusedInputSelectionChangedEvent | null>(null);
|
|
130
133
|
|
|
131
134
|
const { height } = useWindowDimensions();
|
|
132
135
|
|
|
@@ -191,9 +194,9 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
191
194
|
return interpolatedScrollTo;
|
|
192
195
|
}
|
|
193
196
|
|
|
194
|
-
if (
|
|
195
|
-
const positionOnScreen = visibleRect -
|
|
196
|
-
const topOfScreen = scrollPosition.value +
|
|
197
|
+
if (point < 0) {
|
|
198
|
+
const positionOnScreen = visibleRect - bottomOffset;
|
|
199
|
+
const topOfScreen = scrollPosition.value + point;
|
|
197
200
|
|
|
198
201
|
scrollTo(
|
|
199
202
|
scrollViewAnimatedRef,
|
|
@@ -222,65 +225,89 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
222
225
|
[extraKeyboardSpace],
|
|
223
226
|
);
|
|
224
227
|
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
"worklet";
|
|
228
|
+
const updateLayoutFromSelection = useCallback(() => {
|
|
229
|
+
"worklet";
|
|
228
230
|
|
|
229
|
-
|
|
230
|
-
const prevLayout = layout.value;
|
|
231
|
+
const customHeight = lastSelection.value?.selection.end.y;
|
|
231
232
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
if (!input.value?.layout || !customHeight) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
235
236
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
layout
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
[maybeScroll],
|
|
250
|
-
);
|
|
251
|
-
const onChangeText = useCallback(() => {
|
|
237
|
+
layout.value = {
|
|
238
|
+
...input.value,
|
|
239
|
+
layout: {
|
|
240
|
+
...input.value.layout,
|
|
241
|
+
// when we have multiline input with limited amount of lines, then custom height can be very big
|
|
242
|
+
// so we clamp it to max input height
|
|
243
|
+
height: clamp(customHeight, 0, input.value.layout.height),
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return true;
|
|
248
|
+
}, [input, lastSelection, layout]);
|
|
249
|
+
const scrollFromCurrentPosition = useCallback(() => {
|
|
252
250
|
"worklet";
|
|
253
251
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
const prevScrollPosition = scrollPosition.value;
|
|
253
|
+
const prevLayout = layout.value;
|
|
254
|
+
|
|
255
|
+
if (!updateLayoutFromSelection()) {
|
|
257
256
|
return;
|
|
258
257
|
}
|
|
259
258
|
|
|
259
|
+
// eslint-disable-next-line react-compiler/react-compiler
|
|
260
|
+
scrollPosition.value = position.value;
|
|
261
|
+
maybeScroll(keyboardHeight.value, true);
|
|
262
|
+
scrollPosition.value = prevScrollPosition;
|
|
263
|
+
layout.value = prevLayout;
|
|
264
|
+
}, [maybeScroll]);
|
|
265
|
+
const onChangeText = useCallback(() => {
|
|
266
|
+
"worklet";
|
|
267
|
+
|
|
268
|
+
console.debug("maybeScroll - onChangeText");
|
|
260
269
|
scrollFromCurrentPosition();
|
|
261
270
|
}, [scrollFromCurrentPosition]);
|
|
271
|
+
const onChangeTextHandler = useMemo(
|
|
272
|
+
() => debounce(onChangeText, 200),
|
|
273
|
+
[onChangeText],
|
|
274
|
+
);
|
|
262
275
|
const onSelectionChange = useCallback(
|
|
263
276
|
(e: FocusedInputSelectionChangedEvent) => {
|
|
264
277
|
"worklet";
|
|
265
278
|
|
|
279
|
+
const lastTarget = lastSelection.value?.target;
|
|
280
|
+
const latestSelection = lastSelection.value?.selection;
|
|
281
|
+
|
|
282
|
+
lastSelection.value = e;
|
|
283
|
+
|
|
284
|
+
if (e.target !== lastTarget) {
|
|
285
|
+
// ignore this event, because "focus changed" event handled in `useSmoothKeyboardHandler`
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
// caret in the end + end coordinates has been changed -> we moved to a new line
|
|
289
|
+
// so input may grow
|
|
290
|
+
if (
|
|
291
|
+
e.selection.end.position === e.selection.start.position &&
|
|
292
|
+
latestSelection?.end.y !== e.selection.end.y
|
|
293
|
+
) {
|
|
294
|
+
return scrollFromCurrentPosition();
|
|
295
|
+
}
|
|
296
|
+
// selection has been changed
|
|
266
297
|
if (e.selection.start.position !== e.selection.end.position) {
|
|
267
|
-
scrollFromCurrentPosition(
|
|
298
|
+
return scrollFromCurrentPosition();
|
|
268
299
|
}
|
|
269
|
-
},
|
|
270
|
-
[scrollFromCurrentPosition],
|
|
271
|
-
);
|
|
272
300
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
[
|
|
301
|
+
onChangeTextHandler();
|
|
302
|
+
},
|
|
303
|
+
[scrollFromCurrentPosition, onChangeTextHandler],
|
|
276
304
|
);
|
|
277
305
|
|
|
278
306
|
useFocusedInputHandler(
|
|
279
307
|
{
|
|
280
|
-
onChangeText: onChangeTextHandler,
|
|
281
308
|
onSelectionChange: onSelectionChange,
|
|
282
309
|
},
|
|
283
|
-
[
|
|
310
|
+
[onSelectionChange],
|
|
284
311
|
);
|
|
285
312
|
|
|
286
313
|
useSmoothKeyboardHandler(
|
|
@@ -322,9 +349,8 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
322
349
|
// focus was changed
|
|
323
350
|
if (focusWasChanged) {
|
|
324
351
|
tag.value = e.target;
|
|
325
|
-
|
|
326
352
|
// save position of focused text input when keyboard starts to move
|
|
327
|
-
|
|
353
|
+
updateLayoutFromSelection();
|
|
328
354
|
// save current scroll position - when keyboard will hide we'll reuse
|
|
329
355
|
// this value to achieve smooth hide effect
|
|
330
356
|
scrollBeforeKeyboardMovement.value = position.value;
|
|
@@ -369,11 +395,10 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
369
395
|
current?.target === previous?.target &&
|
|
370
396
|
current?.layout.height !== previous?.layout.height
|
|
371
397
|
) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
layout.value = prevLayout;
|
|
398
|
+
// input has changed layout - let's check if we need to scroll
|
|
399
|
+
// may happen when you paste text, then onSelectionChange will be
|
|
400
|
+
// fired earlier than text actually changes its layout
|
|
401
|
+
scrollFromCurrentPosition();
|
|
377
402
|
}
|
|
378
403
|
},
|
|
379
404
|
[],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { Animated, StyleSheet, View } from "react-native";
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import { useKeyboardState } from "../../hooks";
|
|
5
5
|
|
|
6
6
|
import type { KeyboardToolbarTheme } from "./types";
|
|
7
7
|
import type { ViewStyle } from "react-native";
|
|
@@ -13,7 +13,7 @@ type ArrowProps = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const ArrowComponent: React.FC<ArrowProps> = ({ type, disabled, theme }) => {
|
|
16
|
-
const colorScheme =
|
|
16
|
+
const colorScheme = useKeyboardState((state) => state.appearance);
|
|
17
17
|
|
|
18
18
|
const color = useMemo(
|
|
19
19
|
() => ({
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
View,
|
|
7
7
|
} from "react-native";
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import { useKeyboardState } from "../../hooks";
|
|
10
10
|
|
|
11
11
|
import type { KeyboardToolbarTheme } from "./types";
|
|
12
12
|
import type { PropsWithChildren } from "react";
|
|
@@ -64,7 +64,7 @@ const ButtonAndroid = ({
|
|
|
64
64
|
style,
|
|
65
65
|
theme,
|
|
66
66
|
}: PropsWithChildren<ButtonProps>) => {
|
|
67
|
-
const colorScheme =
|
|
67
|
+
const colorScheme = useKeyboardState((state) => state.appearance);
|
|
68
68
|
const accessibilityState = useMemo(() => ({ disabled }), [disabled]);
|
|
69
69
|
const ripple = useMemo(
|
|
70
70
|
() =>
|
|
@@ -2,8 +2,8 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import { StyleSheet, Text, View } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { FocusedInputEvents } from "../../bindings";
|
|
5
|
+
import { useKeyboardState } from "../../hooks";
|
|
5
6
|
import { KeyboardController } from "../../module";
|
|
6
|
-
import useColorScheme from "../hooks/useColorScheme";
|
|
7
7
|
import KeyboardStickyView from "../KeyboardStickyView";
|
|
8
8
|
|
|
9
9
|
import Arrow from "./Arrow";
|
|
@@ -106,7 +106,7 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = (props) => {
|
|
|
106
106
|
insets,
|
|
107
107
|
...rest
|
|
108
108
|
} = props;
|
|
109
|
-
const colorScheme =
|
|
109
|
+
const colorScheme = useKeyboardState((state) => state.appearance);
|
|
110
110
|
const [inputs, setInputs] = useState({
|
|
111
111
|
current: 0,
|
|
112
112
|
count: 0,
|
|
@@ -12,17 +12,25 @@ const getLatestState = () => ({
|
|
|
12
12
|
isVisible: KeyboardController.isVisible(),
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
+
type KeyboardStateSelector<T> = (state: KeyboardState) => T;
|
|
16
|
+
|
|
17
|
+
const defaultSelector: KeyboardStateSelector<KeyboardState> = (state) => state;
|
|
18
|
+
|
|
15
19
|
/**
|
|
16
20
|
* React Hook that represents the current keyboard state on iOS and Android.
|
|
17
21
|
* It tracks keyboard visibility, height, appearance, type and other properties.
|
|
18
22
|
* This hook subscribes to keyboard events and updates the state reactively.
|
|
19
23
|
*
|
|
24
|
+
* @template T - A type of the returned object from the `selector`.
|
|
25
|
+
* @param selector - A function that receives the current keyboard state and picks only necessary properties to avoid frequent re-renders.
|
|
20
26
|
* @returns Object {@link KeyboardState|containing} keyboard state information.
|
|
21
27
|
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/hooks/keyboard/use-keyboard-state|Documentation} page for more details.
|
|
22
28
|
* @example
|
|
23
29
|
* ```tsx
|
|
24
30
|
* function MyComponent() {
|
|
25
|
-
* const
|
|
31
|
+
* const isVisible = useKeyboardState((state) => state.isVisible);
|
|
32
|
+
* const height = useKeyboardState((state) => state.height);
|
|
33
|
+
*
|
|
26
34
|
* return (
|
|
27
35
|
* <View>
|
|
28
36
|
* <Text>Keyboard is {isVisible ? 'visible' : 'hidden'}</Text>
|
|
@@ -32,27 +40,38 @@ const getLatestState = () => ({
|
|
|
32
40
|
* }
|
|
33
41
|
* ```
|
|
34
42
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
function useKeyboardState<T = KeyboardState>(
|
|
44
|
+
selector: KeyboardStateSelector<T> = defaultSelector as KeyboardStateSelector<T>,
|
|
45
|
+
): T {
|
|
46
|
+
const [state, setState] = useState<T>(() => selector(getLatestState()));
|
|
37
47
|
|
|
38
48
|
useEffect(() => {
|
|
39
49
|
const subscriptions = EVENTS.map((event) =>
|
|
40
50
|
KeyboardEvents.addListener(event, () =>
|
|
41
51
|
// state will be updated by global listener first,
|
|
42
52
|
// so we simply read it and don't derive data from the event
|
|
43
|
-
setState(getLatestState),
|
|
53
|
+
setState(selector(getLatestState())),
|
|
44
54
|
),
|
|
45
55
|
);
|
|
56
|
+
// update `appearance` prematurely
|
|
57
|
+
const willShowSubscription = KeyboardEvents.addListener(
|
|
58
|
+
"keyboardWillShow",
|
|
59
|
+
(e) =>
|
|
60
|
+
setState(selector({ ...getLatestState(), appearance: e.appearance })),
|
|
61
|
+
);
|
|
46
62
|
|
|
47
63
|
// we might have missed an update between reading a value in render and
|
|
48
64
|
// `addListener` in this handler, so we set it here. If there was
|
|
49
65
|
// no change, React will filter out this update as a no-op.
|
|
50
|
-
setState(getLatestState);
|
|
66
|
+
setState(selector(getLatestState()));
|
|
51
67
|
|
|
52
68
|
return () => {
|
|
53
69
|
subscriptions.forEach((subscription) => subscription.remove());
|
|
70
|
+
willShowSubscription.remove();
|
|
54
71
|
};
|
|
55
72
|
}, []);
|
|
56
73
|
|
|
57
74
|
return state;
|
|
58
|
-
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { useKeyboardState };
|
package/src/index.ts
CHANGED
package/src/module.ts
CHANGED
|
@@ -13,7 +13,7 @@ let lastState: KeyboardEventData = {
|
|
|
13
13
|
timestamp: new Date().getTime(),
|
|
14
14
|
target: -1,
|
|
15
15
|
type: "default",
|
|
16
|
-
appearance: "
|
|
16
|
+
appearance: "light",
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
KeyboardEvents.addListener("keyboardDidHide", (e) => {
|
|
@@ -54,6 +54,7 @@ export const KeyboardController: KeyboardControllerModule = {
|
|
|
54
54
|
setDefaultMode: KeyboardControllerNative.setDefaultMode,
|
|
55
55
|
setInputMode: KeyboardControllerNative.setInputMode,
|
|
56
56
|
setFocusTo: KeyboardControllerNative.setFocusTo,
|
|
57
|
+
preload: KeyboardControllerNative.preload,
|
|
57
58
|
dismiss,
|
|
58
59
|
isVisible,
|
|
59
60
|
state,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
|
|
2
|
+
|
|
3
|
+
import type { HostComponent } from "react-native";
|
|
4
|
+
import type { ViewProps } from "react-native/Libraries/Components/View/ViewPropTypes";
|
|
5
|
+
|
|
6
|
+
export interface NativeProps extends ViewProps {}
|
|
7
|
+
|
|
8
|
+
export default codegenNativeComponent<NativeProps>("KeyboardBackgroundView", {
|
|
9
|
+
interfaceOnly: true,
|
|
10
|
+
}) as HostComponent<NativeProps>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent";
|
|
2
|
+
|
|
3
|
+
import type { HostComponent } from "react-native";
|
|
4
|
+
import type { ViewProps } from "react-native/Libraries/Components/View/ViewPropTypes";
|
|
5
|
+
|
|
6
|
+
export interface NativeProps extends ViewProps {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default codegenNativeComponent<NativeProps>("KeyboardExtender", {
|
|
11
|
+
interfaceOnly: true,
|
|
12
|
+
excludedPlatforms: ["android"],
|
|
13
|
+
}) as HostComponent<NativeProps>;
|
package/src/types/module.ts
CHANGED
|
@@ -17,8 +17,8 @@ export type KeyboardEventData = {
|
|
|
17
17
|
target: number;
|
|
18
18
|
/** `keyboardType` property from focused `TextInput`. */
|
|
19
19
|
type: NonNullable<TextInputProps["keyboardType"]>;
|
|
20
|
-
/** Keyboard appearance. Can be one of `
|
|
21
|
-
appearance:
|
|
20
|
+
/** Keyboard appearance. Can be one of `dark` or `light`. */
|
|
21
|
+
appearance: "dark" | "light";
|
|
22
22
|
};
|
|
23
23
|
/**
|
|
24
24
|
* An object that represent current keyboard state.
|
|
@@ -59,13 +59,25 @@ export type KeyboardControllerModule = {
|
|
|
59
59
|
/**
|
|
60
60
|
* Sets default `windowSoftInputMode` (the one that declared in `AndroidManifest.xml`).
|
|
61
61
|
*
|
|
62
|
+
* @platform android
|
|
62
63
|
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller#setdefaultmode-|docs} page for more details.
|
|
63
64
|
*/
|
|
64
65
|
setDefaultMode: () => void;
|
|
65
66
|
/**
|
|
66
|
-
* Changes `windowSoftInputMode` on Android.
|
|
67
|
+
* Changes `windowSoftInputMode` on Android.
|
|
68
|
+
*
|
|
69
|
+
* @platform android
|
|
70
|
+
* @see AndroidSoftInputModes for all possible values and {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller#setinputmode-|docs} page for more details.
|
|
67
71
|
*/
|
|
68
72
|
setInputMode: (mode: number) => void;
|
|
73
|
+
// ios only
|
|
74
|
+
/**
|
|
75
|
+
* A method that preloads the keyboard. It's useful when you want to avoid a delay when the user focuses the first input.
|
|
76
|
+
*
|
|
77
|
+
* @platform ios
|
|
78
|
+
* @see {@link https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-controller#preload-|docs} page for more details.
|
|
79
|
+
*/
|
|
80
|
+
preload: () => void;
|
|
69
81
|
// all platforms
|
|
70
82
|
/**
|
|
71
83
|
* Dismisses the active keyboard. Removes a focus by default, but allows to pass `{keepFocus: true}` to keep focus.
|
|
@@ -96,6 +108,8 @@ export type KeyboardControllerNativeModule = {
|
|
|
96
108
|
// android only
|
|
97
109
|
setDefaultMode: () => void;
|
|
98
110
|
setInputMode: (mode: number) => void;
|
|
111
|
+
// ios only
|
|
112
|
+
preload: () => void;
|
|
99
113
|
// all platforms
|
|
100
114
|
dismiss: (keepFocus: boolean) => void;
|
|
101
115
|
setFocusTo: (direction: Direction) => void;
|
package/src/types/views.ts
CHANGED
|
@@ -44,3 +44,8 @@ export type OverKeyboardViewProps = PropsWithChildren<{
|
|
|
44
44
|
*/
|
|
45
45
|
visible: boolean;
|
|
46
46
|
}>;
|
|
47
|
+
export type KeyboardBackgroundViewProps = PropsWithChildren<ViewProps>;
|
|
48
|
+
export type KeyboardExtenderProps = PropsWithChildren<{
|
|
49
|
+
/** Controls whether this `KeyboardExtender` instance should take an effect. Default is `true`. */
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
}>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { RCTKeyboardExtender } from "../../bindings";
|
|
5
|
+
import { useWindowDimensions } from "../../hooks";
|
|
6
|
+
|
|
7
|
+
import type { KeyboardExtenderProps } from "../../types";
|
|
8
|
+
import type { PropsWithChildren } from "react";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A component that embeds its children into the keyboard thus enhancing keyboard functionality.
|
|
12
|
+
*
|
|
13
|
+
* @param props - Component props.
|
|
14
|
+
* @returns A view component that renders inside the keyboard above all system buttons.
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <KeyboardExtender>
|
|
18
|
+
* <Button>10$</Button>
|
|
19
|
+
* <Button>20$</Button>
|
|
20
|
+
* <Button>50$</Button>
|
|
21
|
+
* </KeyboardExtender>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
const KeyboardExtender = (props: PropsWithChildren<KeyboardExtenderProps>) => {
|
|
25
|
+
const { children, enabled = true } = props;
|
|
26
|
+
const { width } = useWindowDimensions();
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<RCTKeyboardExtender enabled={enabled}>
|
|
30
|
+
<View style={{ width }}>{children}</View>
|
|
31
|
+
</RCTKeyboardExtender>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default KeyboardExtender;
|