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.
Files changed (169) hide show
  1. package/README.md +4 -1
  2. package/android/src/base/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +3 -2
  3. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardBackgroundViewManager.kt +19 -0
  4. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +4 -0
  5. package/android/src/fabric/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +3 -1
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/Context.kt +20 -0
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +2 -12
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardBackgroundViewManagerImpl.kt +13 -0
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +1 -1
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +4 -0
  11. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/{StatusBarManagerCompatModuleImpl.kt → statusbar/StatusBarManagerCompatModuleImpl.kt} +31 -5
  12. package/android/src/main/java/com/reactnativekeyboardcontroller/modules/statusbar/StatusBarModuleProxy.kt +86 -0
  13. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +9 -11
  14. package/android/src/main/java/com/reactnativekeyboardcontroller/views/background/KeyboardBackgroundViewGroup.kt +35 -0
  15. package/android/src/main/java/com/reactnativekeyboardcontroller/views/background/Skins.kt +91 -0
  16. package/android/src/main/jni/reactnativekeyboardcontroller.h +1 -0
  17. package/android/src/main/res/values/attrs.xml +7 -0
  18. package/android/src/main/res/values/colors.xml +35 -0
  19. package/android/src/main/res/values-v29/colors.xml +5 -0
  20. package/android/src/main/res/values-v30/colors.xml +17 -0
  21. package/android/src/main/res/values-v33/colors.xml +16 -0
  22. package/android/src/main/res/values-v34/colors.xml +18 -0
  23. package/android/src/main/res/values-v35/colors.xml +10 -0
  24. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardBackgroundViewManager.kt +15 -0
  25. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +5 -0
  26. package/android/src/paper/java/com/reactnativekeyboardcontroller/StatusBarManagerCompatModule.kt +3 -1
  27. package/android/src/turbo/java/com/reactnativekeyboardcontroller/KeyboardControllerPackage.kt +2 -2
  28. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewComponentDescriptor.h +27 -0
  29. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewShadowNode.cpp +14 -0
  30. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewShadowNode.h +30 -0
  31. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardBackgroundViewState.h +28 -0
  32. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderComponentDescriptor.h +27 -0
  33. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderShadowNode.cpp +14 -0
  34. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderShadowNode.h +30 -0
  35. package/common/cpp/react/renderer/components/reactnativekeyboardcontroller/RNKCKeyboardExtenderState.h +28 -0
  36. package/ios/KeyboardControllerModule.mm +9 -0
  37. package/ios/delegates/KCTextInputCompositeDelegate.swift +5 -5
  38. package/ios/events/KeyboardEventEmitterPayload.swift +1 -1
  39. package/ios/extensions/UIResponder.swift +27 -0
  40. package/ios/interactive/KeyboardAreaExtender.swift +5 -5
  41. package/ios/observers/FocusedInputObserver.swift +14 -5
  42. package/ios/observers/KeyboardMovementObserver.swift +7 -1
  43. package/ios/protocols/TextInput.swift +22 -0
  44. package/ios/swizzling/UIResponderSwizzle.swift +3 -0
  45. package/ios/views/KeyboardBackgroundViewManager.h +29 -0
  46. package/ios/views/KeyboardBackgroundViewManager.mm +168 -0
  47. package/ios/views/KeyboardExtenderContainerView.swift +59 -0
  48. package/ios/views/KeyboardExtenderManager.h +29 -0
  49. package/ios/views/KeyboardExtenderManager.mm +287 -0
  50. package/jest/index.js +6 -1
  51. package/lib/commonjs/animated.js +7 -10
  52. package/lib/commonjs/animated.js.map +1 -1
  53. package/lib/commonjs/bindings.js +15 -1
  54. package/lib/commonjs/bindings.js.map +1 -1
  55. package/lib/commonjs/bindings.native.js +5 -1
  56. package/lib/commonjs/bindings.native.js.map +1 -1
  57. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +49 -29
  58. package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
  59. package/lib/commonjs/components/KeyboardToolbar/Arrow.js +2 -3
  60. package/lib/commonjs/components/KeyboardToolbar/Arrow.js.map +1 -1
  61. package/lib/commonjs/components/KeyboardToolbar/Button.js +2 -3
  62. package/lib/commonjs/components/KeyboardToolbar/Button.js.map +1 -1
  63. package/lib/commonjs/components/KeyboardToolbar/index.js +2 -2
  64. package/lib/commonjs/components/KeyboardToolbar/index.js.map +1 -1
  65. package/lib/commonjs/hooks/useKeyboardState/index.js +18 -8
  66. package/lib/commonjs/hooks/useKeyboardState/index.js.map +1 -1
  67. package/lib/commonjs/index.js +8 -1
  68. package/lib/commonjs/index.js.map +1 -1
  69. package/lib/commonjs/module.js +2 -1
  70. package/lib/commonjs/module.js.map +1 -1
  71. package/lib/commonjs/specs/KeyboardBackgroundViewNativeComponent.js +12 -0
  72. package/lib/commonjs/specs/KeyboardBackgroundViewNativeComponent.js.map +1 -0
  73. package/lib/commonjs/specs/KeyboardExtenderNativeComponent.js +13 -0
  74. package/lib/commonjs/specs/KeyboardExtenderNativeComponent.js.map +1 -0
  75. package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
  76. package/lib/commonjs/types/module.js.map +1 -1
  77. package/lib/commonjs/types/views.js.map +1 -1
  78. package/lib/commonjs/views/KeyboardExtender/index.ios.js +43 -0
  79. package/lib/commonjs/views/KeyboardExtender/index.ios.js.map +1 -0
  80. package/lib/commonjs/views/KeyboardExtender/index.js +46 -0
  81. package/lib/commonjs/views/KeyboardExtender/index.js.map +1 -0
  82. package/lib/commonjs/views/index.js +7 -0
  83. package/lib/commonjs/views/index.js.map +1 -1
  84. package/lib/module/animated.js +8 -11
  85. package/lib/module/animated.js.map +1 -1
  86. package/lib/module/bindings.js +14 -0
  87. package/lib/module/bindings.js.map +1 -1
  88. package/lib/module/bindings.native.js +4 -0
  89. package/lib/module/bindings.native.js.map +1 -1
  90. package/lib/module/components/KeyboardAwareScrollView/index.js +50 -30
  91. package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
  92. package/lib/module/components/KeyboardToolbar/Arrow.js +2 -2
  93. package/lib/module/components/KeyboardToolbar/Arrow.js.map +1 -1
  94. package/lib/module/components/KeyboardToolbar/Button.js +2 -2
  95. package/lib/module/components/KeyboardToolbar/Button.js.map +1 -1
  96. package/lib/module/components/KeyboardToolbar/index.js +2 -2
  97. package/lib/module/components/KeyboardToolbar/index.js.map +1 -1
  98. package/lib/module/hooks/useKeyboardState/index.js +18 -6
  99. package/lib/module/hooks/useKeyboardState/index.js.map +1 -1
  100. package/lib/module/index.js +1 -1
  101. package/lib/module/index.js.map +1 -1
  102. package/lib/module/module.js +2 -1
  103. package/lib/module/module.js.map +1 -1
  104. package/lib/module/specs/KeyboardBackgroundViewNativeComponent.js +5 -0
  105. package/lib/module/specs/KeyboardBackgroundViewNativeComponent.js.map +1 -0
  106. package/lib/module/specs/KeyboardExtenderNativeComponent.js +6 -0
  107. package/lib/module/specs/KeyboardExtenderNativeComponent.js.map +1 -0
  108. package/lib/module/specs/NativeKeyboardController.js.map +1 -1
  109. package/lib/module/types/module.js.map +1 -1
  110. package/lib/module/types/views.js.map +1 -1
  111. package/lib/module/views/KeyboardExtender/index.ios.js +36 -0
  112. package/lib/module/views/KeyboardExtender/index.ios.js.map +1 -0
  113. package/lib/module/views/KeyboardExtender/index.js +39 -0
  114. package/lib/module/views/KeyboardExtender/index.js.map +1 -0
  115. package/lib/module/views/index.js +1 -0
  116. package/lib/module/views/index.js.map +1 -1
  117. package/lib/typescript/animated.d.ts +7 -0
  118. package/lib/typescript/bindings.d.ts +14 -1
  119. package/lib/typescript/bindings.native.d.ts +3 -1
  120. package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +2 -2
  121. package/lib/typescript/hooks/useKeyboardState/index.d.ts +8 -2
  122. package/lib/typescript/index.d.ts +1 -1
  123. package/lib/typescript/specs/KeyboardBackgroundViewNativeComponent.d.ts +6 -0
  124. package/lib/typescript/specs/KeyboardExtenderNativeComponent.d.ts +7 -0
  125. package/lib/typescript/specs/NativeKeyboardController.d.ts +1 -0
  126. package/lib/typescript/types/module.d.ts +15 -3
  127. package/lib/typescript/types/views.d.ts +5 -0
  128. package/lib/typescript/views/KeyboardExtender/index.d.ts +19 -0
  129. package/lib/typescript/views/KeyboardExtender/index.ios.d.ts +19 -0
  130. package/lib/typescript/views/index.d.ts +1 -0
  131. package/package.json +7 -3
  132. package/react-native.config.js +1 -0
  133. package/src/animated.tsx +14 -9
  134. package/src/bindings.native.ts +8 -0
  135. package/src/bindings.ts +18 -0
  136. package/src/components/KeyboardAwareScrollView/index.tsx +72 -47
  137. package/src/components/KeyboardToolbar/Arrow.tsx +2 -2
  138. package/src/components/KeyboardToolbar/Button.tsx +2 -2
  139. package/src/components/KeyboardToolbar/index.tsx +2 -2
  140. package/src/hooks/useKeyboardState/index.ts +25 -6
  141. package/src/index.ts +1 -1
  142. package/src/module.ts +2 -1
  143. package/src/specs/KeyboardBackgroundViewNativeComponent.ts +10 -0
  144. package/src/specs/KeyboardExtenderNativeComponent.ts +13 -0
  145. package/src/specs/NativeKeyboardController.ts +1 -0
  146. package/src/types/module.ts +17 -3
  147. package/src/types/views.ts +5 -0
  148. package/src/views/KeyboardExtender/index.ios.tsx +35 -0
  149. package/src/views/KeyboardExtender/index.tsx +42 -0
  150. package/src/views/index.ts +1 -0
  151. package/ios/extensions/UIKeyboardAppearance.swift +0 -21
  152. package/lib/commonjs/components/hooks/useColorScheme.js +0 -10
  153. package/lib/commonjs/components/hooks/useColorScheme.js.map +0 -1
  154. package/lib/commonjs/monkey-patch.android.js +0 -53
  155. package/lib/commonjs/monkey-patch.android.js.map +0 -1
  156. package/lib/commonjs/monkey-patch.js +0 -11
  157. package/lib/commonjs/monkey-patch.js.map +0 -1
  158. package/lib/module/components/hooks/useColorScheme.js +0 -4
  159. package/lib/module/components/hooks/useColorScheme.js.map +0 -1
  160. package/lib/module/monkey-patch.android.js +0 -42
  161. package/lib/module/monkey-patch.android.js.map +0 -1
  162. package/lib/module/monkey-patch.js +0 -5
  163. package/lib/module/monkey-patch.js.map +0 -1
  164. package/lib/typescript/components/hooks/useColorScheme.d.ts +0 -2
  165. package/lib/typescript/monkey-patch.android.d.ts +0 -2
  166. package/lib/typescript/monkey-patch.d.ts +0 -2
  167. package/src/components/hooks/useColorScheme.ts +0 -5
  168. package/src/monkey-patch.android.ts +0 -41
  169. 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.fn().mockReturnValue(state),
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
@@ -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 _monkeyPatch = require("./monkey-patch");
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
- // layout effects
149
- (0, _react.useLayoutEffect)(() => {
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
- }, [enabled]);
152
+ }, [preload]);
156
153
  if (__DEV__) {
157
154
  (0, _reactNativeIsEdgeToEdge.controlEdgeToEdgeValues)({
158
155
  statusBarTranslucent,