react-native-keyboard-controller 1.17.5 → 1.18.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 +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 +1 -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 +168 -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,168 @@
|
|
|
1
|
+
//
|
|
2
|
+
// KeyboardBackgroundViewManager.mm
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 21/04/2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "KeyboardBackgroundViewManager.h"
|
|
9
|
+
|
|
10
|
+
#if __has_include("react_native_keyboard_controller-Swift.h")
|
|
11
|
+
#import "react_native_keyboard_controller-Swift.h"
|
|
12
|
+
#else
|
|
13
|
+
#import <react_native_keyboard_controller/react_native_keyboard_controller-Swift.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
17
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/EventEmitters.h>
|
|
18
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/Props.h>
|
|
19
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/RCTComponentViewHelpers.h>
|
|
20
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewComponentDescriptor.h>
|
|
21
|
+
|
|
22
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
#import <React/RCTView.h>
|
|
26
|
+
#import <UIKit/UIKit.h>
|
|
27
|
+
|
|
28
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
29
|
+
using namespace facebook::react;
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
typedef NS_ENUM(NSInteger, KeyboardBackdropStyle) {
|
|
33
|
+
KeyboardBackdropStyleDark = 2030,
|
|
34
|
+
KeyboardBackdropStyleLight = 3901
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
@protocol KeyboardBackdropViewProtocol <NSObject>
|
|
38
|
+
@optional
|
|
39
|
+
- (instancetype)initWithFrame:(CGRect)frame style:(long long)style;
|
|
40
|
+
- (void)transitionToStyle:(long long)style;
|
|
41
|
+
@end
|
|
42
|
+
|
|
43
|
+
#pragma mark - Manager
|
|
44
|
+
|
|
45
|
+
@implementation KeyboardBackgroundViewManager
|
|
46
|
+
|
|
47
|
+
RCT_EXPORT_MODULE(KeyboardBackgroundViewManager)
|
|
48
|
+
|
|
49
|
+
+ (BOOL)requiresMainQueueSetup
|
|
50
|
+
{
|
|
51
|
+
return NO;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
55
|
+
- (UIView *)view
|
|
56
|
+
{
|
|
57
|
+
RCTView *containerView = [[RCTView alloc] initWithFrame:CGRectZero];
|
|
58
|
+
containerView.clipsToBounds = YES;
|
|
59
|
+
|
|
60
|
+
KeyboardBackgroundView *backgroundView =
|
|
61
|
+
[[KeyboardBackgroundView alloc] initWithFrame:containerView.bounds];
|
|
62
|
+
backgroundView.autoresizingMask =
|
|
63
|
+
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
64
|
+
[containerView addSubview:backgroundView];
|
|
65
|
+
|
|
66
|
+
return containerView;
|
|
67
|
+
}
|
|
68
|
+
#endif
|
|
69
|
+
|
|
70
|
+
@end
|
|
71
|
+
|
|
72
|
+
#pragma mark - View
|
|
73
|
+
|
|
74
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
75
|
+
@interface KeyboardBackgroundView () <RCTKeyboardBackgroundViewViewProtocol>
|
|
76
|
+
#else
|
|
77
|
+
@interface KeyboardBackgroundView ()
|
|
78
|
+
#endif
|
|
79
|
+
@end
|
|
80
|
+
|
|
81
|
+
@implementation KeyboardBackgroundView {
|
|
82
|
+
UIVisualEffectView *_backdropView;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
86
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
87
|
+
{
|
|
88
|
+
return concreteComponentDescriptorProvider<KeyboardBackgroundViewComponentDescriptor>();
|
|
89
|
+
}
|
|
90
|
+
#endif
|
|
91
|
+
|
|
92
|
+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
|
|
93
|
+
+ (void)load
|
|
94
|
+
{
|
|
95
|
+
[super load];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
99
|
+
{
|
|
100
|
+
self = [super initWithFrame:frame];
|
|
101
|
+
if (self) {
|
|
102
|
+
[self setupBackdropView];
|
|
103
|
+
}
|
|
104
|
+
self.clipsToBounds = YES;
|
|
105
|
+
return self;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (void)setupBackdropView
|
|
109
|
+
{
|
|
110
|
+
Class BackdropClass = NSClassFromString(@"UIKBBackdropView");
|
|
111
|
+
if (BackdropClass) {
|
|
112
|
+
long long style = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
|
|
113
|
+
? KeyboardBackdropStyleDark
|
|
114
|
+
: KeyboardBackdropStyleLight;
|
|
115
|
+
|
|
116
|
+
id<KeyboardBackdropViewProtocol> backdrop =
|
|
117
|
+
(id<KeyboardBackdropViewProtocol>)[BackdropClass alloc];
|
|
118
|
+
backdrop = [backdrop initWithFrame:self.bounds style:style];
|
|
119
|
+
|
|
120
|
+
if ([backdrop isKindOfClass:[UIVisualEffectView class]]) {
|
|
121
|
+
_backdropView = (UIVisualEffectView *)backdrop;
|
|
122
|
+
_backdropView.layer.masksToBounds = YES;
|
|
123
|
+
_backdropView.autoresizingMask =
|
|
124
|
+
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
125
|
+
_backdropView.frame = self.bounds;
|
|
126
|
+
[self addSubview:_backdropView];
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
NSLog(@"KeyboardBackdropView class not found");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// MARK: child management
|
|
134
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
135
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
136
|
+
index:(NSInteger)index
|
|
137
|
+
{
|
|
138
|
+
// preserve slot 0 for blur layer
|
|
139
|
+
[super mountChildComponentView:childComponentView index:index + 1];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
143
|
+
index:(NSInteger)index
|
|
144
|
+
{
|
|
145
|
+
// preserve slot 0 for blur layer
|
|
146
|
+
[super unmountChildComponentView:childComponentView index:index + 1];
|
|
147
|
+
}
|
|
148
|
+
#endif
|
|
149
|
+
|
|
150
|
+
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
|
151
|
+
{
|
|
152
|
+
[super traitCollectionDidChange:previousTraitCollection];
|
|
153
|
+
|
|
154
|
+
if (@available(iOS 12.0, *)) {
|
|
155
|
+
if ([previousTraitCollection
|
|
156
|
+
hasDifferentColorAppearanceComparedToTraitCollection:self.traitCollection]) {
|
|
157
|
+
long long style = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark)
|
|
158
|
+
? KeyboardBackdropStyleDark
|
|
159
|
+
: KeyboardBackdropStyleLight;
|
|
160
|
+
|
|
161
|
+
if ([_backdropView respondsToSelector:@selector(transitionToStyle:)]) {
|
|
162
|
+
[(id<KeyboardBackdropViewProtocol>)_backdropView transitionToStyle:style];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// KeyboardExtenderContainerView.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 11/07/2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import UIKit
|
|
9
|
+
|
|
10
|
+
@objc
|
|
11
|
+
public class KeyboardExtenderContainerView: UIView {
|
|
12
|
+
@objc public static func create(frame: CGRect, contentView: UIView) -> UIView {
|
|
13
|
+
if #available(iOS 26.0, *) {
|
|
14
|
+
if let glassEffectClass = NSClassFromString("UIGlassEffect") as? UIVisualEffect.Type {
|
|
15
|
+
let paddingHorizontal = 20.0
|
|
16
|
+
let paddingBottom = 10.0
|
|
17
|
+
let isDark = FocusedInputHolder.shared.get()?.keyboardAppearanceValue == "dark"
|
|
18
|
+
|
|
19
|
+
let glassEffect = glassEffectClass.init()
|
|
20
|
+
let color = isDark ? UIColor.black.withAlphaComponent(0.3) : UIColor.gray.withAlphaComponent(0.3)
|
|
21
|
+
glassEffect.setValue(color, forKey: "tintColor")
|
|
22
|
+
glassEffect.setValue(true, forKey: "interactive")
|
|
23
|
+
|
|
24
|
+
// wrapper container will be full frame
|
|
25
|
+
let wrapperView = UIView(frame: frame)
|
|
26
|
+
wrapperView.backgroundColor = .clear
|
|
27
|
+
|
|
28
|
+
let innerWidth = frame.width - paddingHorizontal * 2
|
|
29
|
+
let innerHeight = frame.height
|
|
30
|
+
|
|
31
|
+
let visualEffectView = UIVisualEffectView(effect: glassEffect)
|
|
32
|
+
visualEffectView.overrideUserInterfaceStyle = isDark ? .dark : .light
|
|
33
|
+
|
|
34
|
+
visualEffectView.frame = CGRect(
|
|
35
|
+
x: paddingHorizontal,
|
|
36
|
+
y: -paddingBottom,
|
|
37
|
+
width: innerWidth,
|
|
38
|
+
height: innerHeight
|
|
39
|
+
)
|
|
40
|
+
contentView.frame = CGRect(
|
|
41
|
+
x: -paddingHorizontal,
|
|
42
|
+
y: 0,
|
|
43
|
+
width: innerWidth,
|
|
44
|
+
height: innerHeight
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
visualEffectView.contentView.addSubview(contentView)
|
|
48
|
+
wrapperView.addSubview(visualEffectView)
|
|
49
|
+
|
|
50
|
+
return wrapperView
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let inputView = UIInputView(frame: frame, inputViewStyle: .keyboard)
|
|
55
|
+
contentView.frame = inputView.bounds
|
|
56
|
+
inputView.addSubview(contentView)
|
|
57
|
+
return inputView
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// KeyboardExtenderManager.h
|
|
3
|
+
// KeyboardController
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 13.04.25.
|
|
6
|
+
// Copyright © 2025 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
9
|
+
#import <React/RCTViewComponentView.h>
|
|
10
|
+
#else
|
|
11
|
+
#import <React/RCTBridge.h>
|
|
12
|
+
#endif
|
|
13
|
+
#import <React/RCTViewManager.h>
|
|
14
|
+
#import <UIKit/UIKit.h>
|
|
15
|
+
|
|
16
|
+
@interface KeyboardExtenderManager : RCTViewManager
|
|
17
|
+
@end
|
|
18
|
+
|
|
19
|
+
@interface KeyboardExtender :
|
|
20
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
21
|
+
RCTViewComponentView
|
|
22
|
+
#else
|
|
23
|
+
UIView
|
|
24
|
+
|
|
25
|
+
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
|
26
|
+
|
|
27
|
+
#endif
|
|
28
|
+
@property (nonatomic, assign) BOOL enabled;
|
|
29
|
+
@end
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
//
|
|
2
|
+
// KeyboardExtenderManager.mm
|
|
3
|
+
// react-native-keyboard-controller
|
|
4
|
+
//
|
|
5
|
+
// Created by Kiryl Ziusko on 13/04/2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "KeyboardExtenderManager.h"
|
|
9
|
+
|
|
10
|
+
#if __has_include("react_native_keyboard_controller-Swift.h")
|
|
11
|
+
#import "react_native_keyboard_controller-Swift.h"
|
|
12
|
+
#else
|
|
13
|
+
#import <react_native_keyboard_controller/react_native_keyboard_controller-Swift.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
17
|
+
#import <React/RCTSurfaceTouchHandler.h>
|
|
18
|
+
|
|
19
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/EventEmitters.h>
|
|
20
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/Props.h>
|
|
21
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/RCTComponentViewHelpers.h>
|
|
22
|
+
#import <react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderComponentDescriptor.h>
|
|
23
|
+
|
|
24
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#import <React/RCTTouchHandler.h>
|
|
28
|
+
#import <UIKit/UIKit.h>
|
|
29
|
+
|
|
30
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
31
|
+
using namespace facebook::react;
|
|
32
|
+
#endif
|
|
33
|
+
|
|
34
|
+
// MARK: Manager
|
|
35
|
+
@implementation KeyboardExtenderManager
|
|
36
|
+
|
|
37
|
+
RCT_EXPORT_MODULE(KeyboardExtenderManager)
|
|
38
|
+
|
|
39
|
+
// Expose the `enabled` prop to React Native
|
|
40
|
+
RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
|
|
41
|
+
|
|
42
|
+
+ (BOOL)requiresMainQueueSetup
|
|
43
|
+
{
|
|
44
|
+
return NO;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#ifndef RCT_NEW_ARCH_ENABLED
|
|
48
|
+
- (UIView *)view
|
|
49
|
+
{
|
|
50
|
+
return [[KeyboardExtender alloc] initWithBridge:self.bridge];
|
|
51
|
+
}
|
|
52
|
+
#endif
|
|
53
|
+
|
|
54
|
+
@end
|
|
55
|
+
|
|
56
|
+
// MARK: View
|
|
57
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
58
|
+
@interface KeyboardExtender () <RCTKeyboardExtenderViewProtocol>
|
|
59
|
+
@end
|
|
60
|
+
#endif
|
|
61
|
+
|
|
62
|
+
@implementation KeyboardExtender {
|
|
63
|
+
UIView *_contentView;
|
|
64
|
+
UIView *_sharedInputAccessoryView;
|
|
65
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
66
|
+
RCTSurfaceTouchHandler *_touchHandler;
|
|
67
|
+
#else
|
|
68
|
+
RCTTouchHandler *_touchHandler;
|
|
69
|
+
#endif
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
73
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
74
|
+
{
|
|
75
|
+
return concreteComponentDescriptorProvider<KeyboardExtenderComponentDescriptor>();
|
|
76
|
+
}
|
|
77
|
+
#endif
|
|
78
|
+
|
|
79
|
+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
|
|
80
|
+
+ (void)load
|
|
81
|
+
{
|
|
82
|
+
[super load];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// MARK: Constructor
|
|
86
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
87
|
+
- (instancetype)init
|
|
88
|
+
{
|
|
89
|
+
if (self = [super init]) {
|
|
90
|
+
_touchHandler = [RCTSurfaceTouchHandler new];
|
|
91
|
+
_contentView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
92
|
+
}
|
|
93
|
+
#else
|
|
94
|
+
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
|
95
|
+
{
|
|
96
|
+
self = [super initWithFrame:CGRectZero];
|
|
97
|
+
if (self) {
|
|
98
|
+
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge];
|
|
99
|
+
_contentView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
100
|
+
}
|
|
101
|
+
#endif
|
|
102
|
+
[_touchHandler attachToView:_contentView];
|
|
103
|
+
[self setupObservers];
|
|
104
|
+
return self;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
- (void)dealloc
|
|
108
|
+
{
|
|
109
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// MARK: Listeners
|
|
113
|
+
- (void)setupObservers
|
|
114
|
+
{
|
|
115
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
116
|
+
selector:@selector(handleTextInputDidBeginEditing:)
|
|
117
|
+
name:UITextFieldTextDidBeginEditingNotification
|
|
118
|
+
object:nil];
|
|
119
|
+
|
|
120
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
121
|
+
selector:@selector(handleTextInputDidBeginEditing:)
|
|
122
|
+
name:UITextViewTextDidBeginEditingNotification
|
|
123
|
+
object:nil];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
- (void)handleTextInputDidBeginEditing:(NSNotification *)notification
|
|
127
|
+
{
|
|
128
|
+
if (self.enabled) {
|
|
129
|
+
[self attachToTextInput:(UIView *)notification.object];
|
|
130
|
+
} else {
|
|
131
|
+
[self detachInputAccessoryView];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
- (void)attachToTextInput:(UIView *)textInput
|
|
136
|
+
{
|
|
137
|
+
if ([textInput isKindOfClass:[UITextField class]]) {
|
|
138
|
+
[self attachInputAccessoryViewTo:(UITextField *)textInput];
|
|
139
|
+
} else if ([textInput isKindOfClass:[UITextView class]]) {
|
|
140
|
+
[self attachInputAccessoryViewTo:(UITextView *)textInput];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (void)createSharedInputAccessoryView
|
|
145
|
+
{
|
|
146
|
+
CGRect internalFrame = _contentView.subviews[0].frame;
|
|
147
|
+
_sharedInputAccessoryView = [KeyboardExtenderContainerView
|
|
148
|
+
createWithFrame:CGRectMake(
|
|
149
|
+
0, 0, UIScreen.mainScreen.bounds.size.width, internalFrame.size.height)
|
|
150
|
+
contentView:_contentView];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
- (void)attachInputAccessoryViewTo:(UIView<UITextInput> *)input
|
|
154
|
+
{
|
|
155
|
+
// Initialize the shared input accessory view once
|
|
156
|
+
if (!_sharedInputAccessoryView) {
|
|
157
|
+
[self createSharedInputAccessoryView];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Assign the inputAccessoryView
|
|
161
|
+
if ([input isKindOfClass:[UITextField class]]) {
|
|
162
|
+
((UITextField *)input).inputAccessoryView = _sharedInputAccessoryView;
|
|
163
|
+
} else if ([input isKindOfClass:[UITextView class]]) {
|
|
164
|
+
((UITextView *)input).inputAccessoryView = _sharedInputAccessoryView;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Refresh input view to apply changes
|
|
168
|
+
[input reloadInputViews];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
- (void)detachInputAccessoryView
|
|
172
|
+
{
|
|
173
|
+
// Remove the accessory view from the current text input
|
|
174
|
+
UIResponder *firstResponder = [UIResponder current];
|
|
175
|
+
if ([firstResponder isKindOfClass:[UITextField class]] ||
|
|
176
|
+
[firstResponder isKindOfClass:[UITextView class]]) {
|
|
177
|
+
UIView<UITextInput> *textInput = (UIView<UITextInput> *)firstResponder;
|
|
178
|
+
if (textInput.inputAccessoryView == _sharedInputAccessoryView) {
|
|
179
|
+
// Assign the inputAccessoryView
|
|
180
|
+
if ([textInput isKindOfClass:[UITextField class]]) {
|
|
181
|
+
((UITextField *)textInput).inputAccessoryView = nil;
|
|
182
|
+
} else if ([textInput isKindOfClass:[UITextView class]]) {
|
|
183
|
+
((UITextView *)textInput).inputAccessoryView = nil;
|
|
184
|
+
}
|
|
185
|
+
[textInput reloadInputViews];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// MARK: touch handling
|
|
191
|
+
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
|
192
|
+
{
|
|
193
|
+
BOOL canReceiveTouchEvents = ([self isUserInteractionEnabled] && ![self isHidden] && _enabled);
|
|
194
|
+
if (!canReceiveTouchEvents) {
|
|
195
|
+
return nil;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// `hitSubview` is the topmost subview which was hit. The hit point can
|
|
199
|
+
// be outside the bounds of `view` (e.g., if -clipsToBounds is NO).
|
|
200
|
+
UIView *hitSubview = nil;
|
|
201
|
+
BOOL isPointInside = [self pointInside:point withEvent:event];
|
|
202
|
+
if (![self clipsToBounds] || isPointInside) {
|
|
203
|
+
// TODO: should we take zIndex into consideration?
|
|
204
|
+
// The default behaviour of UIKit is that if a view does not contain a point,
|
|
205
|
+
// then no subviews will be returned from hit testing, even if they contain
|
|
206
|
+
// the hit point. By doing hit testing directly on the subviews, we bypass
|
|
207
|
+
// the strict containment policy (i.e., UIKit guarantees that every ancestor
|
|
208
|
+
// of the hit view will return YES from -pointInside:withEvent:). See:
|
|
209
|
+
// - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
|
|
210
|
+
for (UIView *subview in [_contentView.subviews reverseObjectEnumerator]) {
|
|
211
|
+
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
|
|
212
|
+
hitSubview = [subview hitTest:convertedPoint withEvent:event];
|
|
213
|
+
if (hitSubview != nil) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return hitSubview;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// MARK: props updater
|
|
222
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
223
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
224
|
+
{
|
|
225
|
+
const auto &oldViewProps = *std::static_pointer_cast<const KeyboardExtenderProps>(_props);
|
|
226
|
+
const auto &newViewProps = *std::static_pointer_cast<const KeyboardExtenderProps>(props);
|
|
227
|
+
|
|
228
|
+
if (newViewProps.enabled != oldViewProps.enabled) {
|
|
229
|
+
[self updateEnabledState:newViewProps.enabled];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
[super updateProps:props oldProps:oldProps];
|
|
233
|
+
}
|
|
234
|
+
#else
|
|
235
|
+
- (void)setEnabled:(BOOL)enabled
|
|
236
|
+
{
|
|
237
|
+
_enabled = enabled;
|
|
238
|
+
|
|
239
|
+
[self updateEnabledState:enabled];
|
|
240
|
+
}
|
|
241
|
+
#endif
|
|
242
|
+
|
|
243
|
+
- (void)updateEnabledState:(BOOL)enabled
|
|
244
|
+
{
|
|
245
|
+
_enabled = enabled;
|
|
246
|
+
|
|
247
|
+
if (_sharedInputAccessoryView) {
|
|
248
|
+
if (!enabled) {
|
|
249
|
+
[self detachInputAccessoryView];
|
|
250
|
+
} else {
|
|
251
|
+
// Re-attach if a text input is active
|
|
252
|
+
UIResponder *firstResponder = [UIResponder current];
|
|
253
|
+
if ([firstResponder conformsToProtocol:@protocol(UITextInput)]) {
|
|
254
|
+
[self attachToTextInput:(UIView *)firstResponder];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// MARK: child management
|
|
261
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
262
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
263
|
+
index:(NSInteger)index
|
|
264
|
+
{
|
|
265
|
+
[_contentView insertSubview:childComponentView atIndex:index];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView
|
|
269
|
+
index:(NSInteger)index
|
|
270
|
+
{
|
|
271
|
+
[childComponentView removeFromSuperview];
|
|
272
|
+
}
|
|
273
|
+
#else
|
|
274
|
+
- (void)addSubview:(UIView *)view
|
|
275
|
+
{
|
|
276
|
+
[_contentView addSubview:view];
|
|
277
|
+
}
|
|
278
|
+
#endif
|
|
279
|
+
|
|
280
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
281
|
+
Class<RCTComponentViewProtocol> KeyboardExtenderCls(void)
|
|
282
|
+
{
|
|
283
|
+
return KeyboardExtender.class;
|
|
284
|
+
}
|
|
285
|
+
#endif
|
|
286
|
+
|
|
287
|
+
@end
|
package/jest/index.js
CHANGED
|
@@ -51,7 +51,9 @@ const mock = {
|
|
|
51
51
|
useGenericKeyboardHandler: jest.fn(),
|
|
52
52
|
useKeyboardHandler: jest.fn(),
|
|
53
53
|
useKeyboardContext: jest.fn().mockReturnValue(values),
|
|
54
|
-
useKeyboardState: jest
|
|
54
|
+
useKeyboardState: jest
|
|
55
|
+
.fn()
|
|
56
|
+
.mockImplementation((selector) => (selector ? selector(state) : state)),
|
|
55
57
|
/// input
|
|
56
58
|
useReanimatedFocusedInput: jest.fn().mockReturnValue(focusedInput),
|
|
57
59
|
useFocusedInputHandler: jest.fn(),
|
|
@@ -65,6 +67,7 @@ const mock = {
|
|
|
65
67
|
KeyboardController: {
|
|
66
68
|
setInputMode: jest.fn(),
|
|
67
69
|
setDefaultMode: jest.fn(),
|
|
70
|
+
preload: jest.fn(),
|
|
68
71
|
dismiss: jest.fn().mockReturnValue(Promise.resolve()),
|
|
69
72
|
setFocusTo: jest.fn(),
|
|
70
73
|
isVisible: jest.fn().mockReturnValue(false),
|
|
@@ -93,6 +96,8 @@ const mock = {
|
|
|
93
96
|
KeyboardControllerView: "KeyboardControllerView",
|
|
94
97
|
KeyboardGestureArea: "KeyboardGestureArea",
|
|
95
98
|
OverKeyboardView: "OverKeyboardView",
|
|
99
|
+
KeyboardBackgroundView: "KeyboardBackgroundView",
|
|
100
|
+
KeyboardExtender: "KeyboardExtender",
|
|
96
101
|
// providers
|
|
97
102
|
KeyboardProvider: "KeyboardProvider",
|
|
98
103
|
// components
|
package/lib/commonjs/animated.js
CHANGED
|
@@ -12,7 +12,7 @@ var _bindings = require("./bindings");
|
|
|
12
12
|
var _context = require("./context");
|
|
13
13
|
var _eventMappings = require("./event-mappings");
|
|
14
14
|
var _internal = require("./internal");
|
|
15
|
-
var
|
|
15
|
+
var _module = require("./module");
|
|
16
16
|
var _reanimated = require("./reanimated");
|
|
17
17
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
18
18
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -53,7 +53,8 @@ const KeyboardProvider = props => {
|
|
|
53
53
|
statusBarTranslucent,
|
|
54
54
|
navigationBarTranslucent,
|
|
55
55
|
preserveEdgeToEdge,
|
|
56
|
-
enabled: initiallyEnabled = true
|
|
56
|
+
enabled: initiallyEnabled = true,
|
|
57
|
+
preload = true
|
|
57
58
|
} = props;
|
|
58
59
|
// ref
|
|
59
60
|
const viewTagRef = (0, _react.useRef)(null);
|
|
@@ -144,15 +145,11 @@ const KeyboardProvider = props => {
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
}, []);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (enabled) {
|
|
151
|
-
(0, _monkeyPatch.applyMonkeyPatch)();
|
|
152
|
-
} else {
|
|
153
|
-
(0, _monkeyPatch.revertMonkeyPatch)();
|
|
148
|
+
(0, _react.useEffect)(() => {
|
|
149
|
+
if (preload) {
|
|
150
|
+
_module.KeyboardController.preload();
|
|
154
151
|
}
|
|
155
|
-
}, [
|
|
152
|
+
}, [preload]);
|
|
156
153
|
if (__DEV__) {
|
|
157
154
|
(0, _reactNativeIsEdgeToEdge.controlEdgeToEdgeValues)({
|
|
158
155
|
statusBarTranslucent,
|