react-native-keyboard-controller 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +2 -0
  2. package/android/gradle.properties +1 -1
  3. package/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +5 -0
  4. package/android/src/main/java/com/reactnativekeyboardcontroller/events/FocusedInputLayoutChangedEvent.kt +41 -0
  5. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/ThemedReactContext.kt +9 -0
  6. package/android/src/main/java/com/reactnativekeyboardcontroller/extensions/View.kt +7 -0
  7. package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/FocusedInputLayoutObserver.kt +93 -0
  8. package/android/src/main/java/com/reactnativekeyboardcontroller/{KeyboardAnimationCallback.kt → listeners/KeyboardAnimationCallback.kt} +86 -55
  9. package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +6 -0
  10. package/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt +120 -50
  11. package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerViewManager.kt +5 -0
  12. package/ios/Extensions.swift +7 -0
  13. package/ios/KeyboardController.xcodeproj/project.pbxproj +41 -7
  14. package/ios/events/FocusedInputLayoutChangedEvent.h +16 -0
  15. package/ios/events/FocusedInputLayoutChangedEvent.m +75 -0
  16. package/ios/observers/FocusedInputLayoutObserver.swift +136 -0
  17. package/ios/{KeyboardControllerView.mm → views/KeyboardControllerView.mm} +54 -3
  18. package/ios/{KeyboardControllerViewManager.mm → views/KeyboardControllerViewManager.mm} +7 -0
  19. package/ios/{KeyboardControllerViewManager.swift → views/KeyboardControllerViewManager.swift} +25 -1
  20. package/jest/index.js +16 -0
  21. package/lib/commonjs/animated.js +34 -6
  22. package/lib/commonjs/animated.js.map +1 -1
  23. package/lib/commonjs/bindings.js.map +1 -1
  24. package/lib/commonjs/bindings.native.js.map +1 -1
  25. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js +9 -1
  26. package/lib/commonjs/components/KeyboardAvoidingView/hooks.js.map +1 -1
  27. package/lib/commonjs/components/KeyboardAvoidingView/index.js +7 -4
  28. package/lib/commonjs/components/KeyboardAvoidingView/index.js.map +1 -1
  29. package/lib/commonjs/components/KeyboardStickyView/index.js +44 -0
  30. package/lib/commonjs/components/KeyboardStickyView/index.js.map +1 -0
  31. package/lib/commonjs/components/index.js +7 -0
  32. package/lib/commonjs/components/index.js.map +1 -1
  33. package/lib/commonjs/constants.js.map +1 -1
  34. package/lib/commonjs/context.js +9 -4
  35. package/lib/commonjs/context.js.map +1 -1
  36. package/lib/commonjs/hooks.js +16 -1
  37. package/lib/commonjs/hooks.js.map +1 -1
  38. package/lib/commonjs/index.js +8 -2
  39. package/lib/commonjs/index.js.map +1 -1
  40. package/lib/commonjs/internal.js.map +1 -1
  41. package/lib/commonjs/monkey-patch.android.js +32 -20
  42. package/lib/commonjs/monkey-patch.android.js.map +1 -1
  43. package/lib/commonjs/monkey-patch.js +11 -1
  44. package/lib/commonjs/monkey-patch.js.map +1 -1
  45. package/lib/commonjs/reanimated.js +5 -2
  46. package/lib/commonjs/reanimated.js.map +1 -1
  47. package/lib/commonjs/reanimated.native.js +18 -1
  48. package/lib/commonjs/reanimated.native.js.map +1 -1
  49. package/lib/commonjs/replicas.js +1 -1
  50. package/lib/commonjs/replicas.js.map +1 -1
  51. package/lib/commonjs/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  52. package/lib/commonjs/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
  53. package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
  54. package/lib/commonjs/specs/NativeStatusBarManagerCompat.js.map +1 -1
  55. package/lib/commonjs/types.js.map +1 -1
  56. package/lib/module/animated.js +37 -9
  57. package/lib/module/animated.js.map +1 -1
  58. package/lib/module/bindings.js.map +1 -1
  59. package/lib/module/bindings.native.js +1 -1
  60. package/lib/module/bindings.native.js.map +1 -1
  61. package/lib/module/components/KeyboardAvoidingView/hooks.js +9 -1
  62. package/lib/module/components/KeyboardAvoidingView/hooks.js.map +1 -1
  63. package/lib/module/components/KeyboardAvoidingView/index.js +9 -6
  64. package/lib/module/components/KeyboardAvoidingView/index.js.map +1 -1
  65. package/lib/module/components/KeyboardStickyView/index.js +35 -0
  66. package/lib/module/components/KeyboardStickyView/index.js.map +1 -0
  67. package/lib/module/components/index.js +1 -0
  68. package/lib/module/components/index.js.map +1 -1
  69. package/lib/module/constants.js.map +1 -1
  70. package/lib/module/context.js +9 -4
  71. package/lib/module/context.js.map +1 -1
  72. package/lib/module/hooks.js +14 -1
  73. package/lib/module/hooks.js.map +1 -1
  74. package/lib/module/index.js +1 -2
  75. package/lib/module/index.js.map +1 -1
  76. package/lib/module/internal.js.map +1 -1
  77. package/lib/module/monkey-patch.android.js +26 -20
  78. package/lib/module/monkey-patch.android.js.map +1 -1
  79. package/lib/module/monkey-patch.js +3 -0
  80. package/lib/module/monkey-patch.js.map +1 -1
  81. package/lib/module/reanimated.js +3 -1
  82. package/lib/module/reanimated.js.map +1 -1
  83. package/lib/module/reanimated.native.js +16 -0
  84. package/lib/module/reanimated.native.js.map +1 -1
  85. package/lib/module/replicas.js +2 -2
  86. package/lib/module/replicas.js.map +1 -1
  87. package/lib/module/specs/KeyboardControllerViewNativeComponent.js.map +1 -1
  88. package/lib/module/specs/KeyboardGestureAreaNativeComponent.js.map +1 -1
  89. package/lib/module/specs/NativeKeyboardController.js.map +1 -1
  90. package/lib/module/specs/NativeStatusBarManagerCompat.js.map +1 -1
  91. package/lib/module/types.js.map +1 -1
  92. package/lib/typescript/animated.d.ts +8 -1
  93. package/lib/typescript/components/KeyboardAvoidingView/hooks.d.ts +1 -0
  94. package/lib/typescript/components/KeyboardAvoidingView/index.d.ts +2 -1
  95. package/lib/typescript/components/KeyboardStickyView/index.d.ts +20 -0
  96. package/lib/typescript/components/index.d.ts +1 -0
  97. package/lib/typescript/context.d.ts +6 -3
  98. package/lib/typescript/hooks.d.ts +9 -2
  99. package/lib/typescript/index.d.ts +1 -2
  100. package/lib/typescript/internal.d.ts +1 -1
  101. package/lib/typescript/monkey-patch.android.d.ts +2 -1
  102. package/lib/typescript/monkey-patch.d.ts +2 -0
  103. package/lib/typescript/reanimated.d.ts +2 -1
  104. package/lib/typescript/reanimated.native.d.ts +2 -1
  105. package/lib/typescript/specs/KeyboardControllerViewNativeComponent.d.ts +13 -0
  106. package/lib/typescript/types.d.ts +17 -0
  107. package/package.json +9 -28
  108. package/src/animated.tsx +53 -9
  109. package/src/bindings.native.ts +1 -1
  110. package/src/bindings.ts +3 -1
  111. package/src/components/KeyboardAvoidingView/hooks.ts +9 -1
  112. package/src/components/KeyboardAvoidingView/index.tsx +15 -13
  113. package/src/components/KeyboardStickyView/index.tsx +63 -0
  114. package/src/components/index.ts +1 -0
  115. package/src/constants.ts +2 -0
  116. package/src/context.ts +16 -5
  117. package/src/hooks.ts +16 -6
  118. package/src/index.ts +1 -3
  119. package/src/internal.ts +3 -1
  120. package/src/monkey-patch.android.ts +27 -21
  121. package/src/monkey-patch.ts +3 -0
  122. package/src/reanimated.native.ts +30 -1
  123. package/src/reanimated.ts +13 -2
  124. package/src/replicas.ts +2 -2
  125. package/src/specs/KeyboardControllerViewNativeComponent.ts +16 -1
  126. package/src/specs/KeyboardGestureAreaNativeComponent.ts +2 -1
  127. package/src/specs/NativeKeyboardController.ts +2 -1
  128. package/src/specs/NativeStatusBarManagerCompat.ts +2 -1
  129. package/src/types.ts +28 -1
  130. /package/ios/{KeyboardMoveEvent.h → events/KeyboardMoveEvent.h} +0 -0
  131. /package/ios/{KeyboardMoveEvent.m → events/KeyboardMoveEvent.m} +0 -0
  132. /package/ios/{KeyboardMovementObserver.swift → observers/KeyboardMovementObserver.swift} +0 -0
  133. /package/ios/{KeyboardControllerView.h → views/KeyboardControllerView.h} +0 -0
@@ -16,6 +16,11 @@ class KeyboardControllerViewManager(mReactContext: ReactApplicationContext) : Re
16
16
  return manager.createViewInstance(reactContext)
17
17
  }
18
18
 
19
+ @ReactProp(name = "enabled")
20
+ fun setEnabled(view: EdgeToEdgeReactViewGroup, enabled: Boolean) {
21
+ manager.setEnabled(view, enabled)
22
+ }
23
+
19
24
  @ReactProp(name = "statusBarTranslucent")
20
25
  fun setStatusBarTranslucent(view: EdgeToEdgeReactViewGroup, isStatusBarTranslucent: Boolean) {
21
26
  manager.setStatusBarTranslucent(view, isStatusBarTranslucent)
@@ -52,3 +52,10 @@ public extension Optional where Wrapped == UIResponder {
52
52
  #endif
53
53
  }
54
54
  }
55
+
56
+ public extension UIView {
57
+ var globalFrame: CGRect? {
58
+ let rootView = UIApplication.shared.keyWindow?.rootViewController?.view
59
+ return superview?.convert(frame, to: rootView)
60
+ }
61
+ }
@@ -8,6 +8,8 @@
8
8
 
9
9
  /* Begin PBXBuildFile section */
10
10
  0807071E2A34807B00C05A19 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0807071D2A34807B00C05A19 /* Extensions.swift */; };
11
+ 084AEEC62ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 084AEEC52ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m */; };
12
+ 084AEEC82ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084AEEC72ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift */; };
11
13
  F333F8D428996B8D0015B05F /* KeyboardControllerView.mm in Sources */ = {isa = PBXBuildFile; fileRef = F333F8D228996B8D0015B05F /* KeyboardControllerView.mm */; };
12
14
  F359D34F28133C26000B6AFE /* KeyboardControllerModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = F359D34E28133C26000B6AFE /* KeyboardControllerModule.mm */; };
13
15
  F3626A0728B3FE760021B2D9 /* KeyboardMovementObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3626A0628B3FE760021B2D9 /* KeyboardMovementObserver.swift */; };
@@ -29,6 +31,9 @@
29
31
 
30
32
  /* Begin PBXFileReference section */
31
33
  0807071D2A34807B00C05A19 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
34
+ 084AEEC22ACF479A001A3069 /* FocusedInputLayoutChangedEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FocusedInputLayoutChangedEvent.h; sourceTree = "<group>"; };
35
+ 084AEEC52ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FocusedInputLayoutChangedEvent.m; sourceTree = "<group>"; };
36
+ 084AEEC72ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedInputLayoutObserver.swift; sourceTree = "<group>"; };
32
37
  134814201AA4EA6300B7C361 /* libKeyboardController.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKeyboardController.a; sourceTree = BUILT_PRODUCTS_DIR; };
33
38
  B3E7B5891CC2AC0600A0062D /* KeyboardControllerViewManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyboardControllerViewManager.mm; sourceTree = "<group>"; };
34
39
  F333F8D128996B1C0015B05F /* KeyboardControllerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyboardControllerView.h; sourceTree = "<group>"; };
@@ -53,6 +58,37 @@
53
58
  /* End PBXFrameworksBuildPhase section */
54
59
 
55
60
  /* Begin PBXGroup section */
61
+ 084AEEBF2ACDFBF1001A3069 /* events */ = {
62
+ isa = PBXGroup;
63
+ children = (
64
+ F3F50667289E653B003091D6 /* KeyboardMoveEvent.h */,
65
+ F3F50668289E653B003091D6 /* KeyboardMoveEvent.m */,
66
+ 084AEEC22ACF479A001A3069 /* FocusedInputLayoutChangedEvent.h */,
67
+ 084AEEC52ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m */,
68
+ );
69
+ path = events;
70
+ sourceTree = "<group>";
71
+ };
72
+ 084AEEC02ACDFC2A001A3069 /* observers */ = {
73
+ isa = PBXGroup;
74
+ children = (
75
+ F3626A0628B3FE760021B2D9 /* KeyboardMovementObserver.swift */,
76
+ 084AEEC72ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift */,
77
+ );
78
+ path = observers;
79
+ sourceTree = "<group>";
80
+ };
81
+ 084AEEC12ACF405C001A3069 /* views */ = {
82
+ isa = PBXGroup;
83
+ children = (
84
+ F333F8D228996B8D0015B05F /* KeyboardControllerView.mm */,
85
+ F333F8D128996B1C0015B05F /* KeyboardControllerView.h */,
86
+ F4FF95D6245B92E800C19C63 /* KeyboardControllerViewManager.swift */,
87
+ B3E7B5891CC2AC0600A0062D /* KeyboardControllerViewManager.mm */,
88
+ );
89
+ path = views;
90
+ sourceTree = "<group>";
91
+ };
56
92
  134814211AA4EA7D00B7C361 /* Products */ = {
57
93
  isa = PBXGroup;
58
94
  children = (
@@ -64,16 +100,12 @@
64
100
  58B511D21A9E6C8500147676 = {
65
101
  isa = PBXGroup;
66
102
  children = (
103
+ 084AEEC12ACF405C001A3069 /* views */,
104
+ 084AEEC02ACDFC2A001A3069 /* observers */,
105
+ 084AEEBF2ACDFBF1001A3069 /* events */,
67
106
  0807071D2A34807B00C05A19 /* Extensions.swift */,
68
- F3F50667289E653B003091D6 /* KeyboardMoveEvent.h */,
69
- F3F50668289E653B003091D6 /* KeyboardMoveEvent.m */,
70
- F3626A0628B3FE760021B2D9 /* KeyboardMovementObserver.swift */,
71
- F333F8D228996B8D0015B05F /* KeyboardControllerView.mm */,
72
- F333F8D128996B1C0015B05F /* KeyboardControllerView.h */,
73
107
  F359D35028133C6F000B6AFE /* KeyboardControllerModule-Header.h */,
74
108
  F359D34E28133C26000B6AFE /* KeyboardControllerModule.mm */,
75
- F4FF95D6245B92E800C19C63 /* KeyboardControllerViewManager.swift */,
76
- B3E7B5891CC2AC0600A0062D /* KeyboardControllerViewManager.mm */,
77
109
  F4FF95D5245B92E700C19C63 /* KeyboardController-Bridging-Header.h */,
78
110
  134814211AA4EA7D00B7C361 /* Products */,
79
111
  );
@@ -136,9 +168,11 @@
136
168
  isa = PBXSourcesBuildPhase;
137
169
  buildActionMask = 2147483647;
138
170
  files = (
171
+ 084AEEC62ACF49A8001A3069 /* FocusedInputLayoutChangedEvent.m in Sources */,
139
172
  F3626A0728B3FE760021B2D9 /* KeyboardMovementObserver.swift in Sources */,
140
173
  0807071E2A34807B00C05A19 /* Extensions.swift in Sources */,
141
174
  F359D34F28133C26000B6AFE /* KeyboardControllerModule.mm in Sources */,
175
+ 084AEEC82ACF4AB2001A3069 /* FocusedInputLayoutObserver.swift in Sources */,
142
176
  F4FF95D7245B92E800C19C63 /* KeyboardControllerViewManager.swift in Sources */,
143
177
  F333F8D428996B8D0015B05F /* KeyboardControllerView.mm in Sources */,
144
178
  F3F50669289E653B003091D6 /* KeyboardMoveEvent.m in Sources */,
@@ -0,0 +1,16 @@
1
+ //
2
+ // FocusedInputLayoutChangedEvent.h
3
+ // KeyboardController
4
+ //
5
+ // Created by Kiryl Ziusko on 05/10/2023.
6
+ // Copyright © 2023 Facebook. All rights reserved.
7
+ //
8
+
9
+ #import <Foundation/Foundation.h>
10
+ #import <React/RCTEventDispatcherProtocol.h>
11
+
12
+ @interface FocusedInputLayoutChangedEvent : NSObject <RCTEvent>
13
+
14
+ - (instancetype)initWithReactTag:(NSNumber *)reactTag event:(NSObject *)event;
15
+
16
+ @end
@@ -0,0 +1,75 @@
1
+ //
2
+ // FocusedInputLayoutChangedEvent.m
3
+ // KeyboardController
4
+ //
5
+ // Created by Kiryl Ziusko on 05/10/2023.
6
+ // Copyright © 2023 Facebook. All rights reserved.
7
+ //
8
+
9
+ #import "FocusedInputLayoutChangedEvent.h"
10
+ #import <React/RCTAssert.h>
11
+
12
+ @implementation FocusedInputLayoutChangedEvent {
13
+ NSNumber *_target;
14
+ NSObject *_layout;
15
+ uint16_t _coalescingKey;
16
+ }
17
+
18
+ - (NSString *)eventName
19
+ {
20
+ return @"onFocusedInputLayoutChanged";
21
+ }
22
+
23
+ @synthesize viewTag = _viewTag;
24
+
25
+ - (instancetype)initWithReactTag:(NSNumber *)reactTag event:(NSObject *)event
26
+ {
27
+ RCTAssertParam(reactTag);
28
+
29
+ if ((self = [super init])) {
30
+ _viewTag = reactTag;
31
+ _target = [event valueForKey:@"target"];
32
+ _layout = [event valueForKey:@"layout"];
33
+ _coalescingKey = 0;
34
+ }
35
+ return self;
36
+ }
37
+
38
+ RCT_NOT_IMPLEMENTED(-(instancetype)init)
39
+
40
+ - (uint16_t)coalescingKey
41
+ {
42
+ return _coalescingKey;
43
+ }
44
+
45
+ - (NSDictionary *)body
46
+ {
47
+ NSDictionary *body = @{
48
+ @"target" : _target,
49
+ @"layout" : _layout,
50
+ };
51
+
52
+ return body;
53
+ }
54
+
55
+ - (BOOL)canCoalesce
56
+ {
57
+ return NO;
58
+ }
59
+
60
+ - (FocusedInputLayoutChangedEvent *)coalesceWithEvent:(FocusedInputLayoutChangedEvent *)newEvent
61
+ {
62
+ return newEvent;
63
+ }
64
+
65
+ + (NSString *)moduleDotMethod
66
+ {
67
+ return @"RCTEventEmitter.receiveEvent";
68
+ }
69
+
70
+ - (NSArray *)arguments
71
+ {
72
+ return @[ self.viewTag, RCTNormalizeInputEventName(self.eventName), [self body] ];
73
+ }
74
+
75
+ @end
@@ -0,0 +1,136 @@
1
+ //
2
+ // FocusedInputLayoutObserver.swift
3
+ // KeyboardController
4
+ //
5
+ // Created by Kiryl Ziusko on 05/10/2023.
6
+ // Copyright © 2023 Facebook. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+ import UIKit
11
+
12
+ let noFocusedInputEvent: [String: Any] = [
13
+ "target": -1,
14
+ "layout": [
15
+ "absoluteX": 0,
16
+ "absoluteY": 0,
17
+ "width": 0,
18
+ "height": 0,
19
+ "x": 0,
20
+ "y": 0,
21
+ ],
22
+ ]
23
+
24
+ @objc(FocusedInputLayoutObserver)
25
+ public class FocusedInputLayoutObserver: NSObject {
26
+ // class members
27
+ var onEvent: (NSDictionary) -> Void
28
+ // input tracking
29
+ private var currentInput: UIView?
30
+ private var hasKVObserver = false
31
+ private var lastEventDispatched: [AnyHashable: Any] = noFocusedInputEvent
32
+
33
+ @objc public init(
34
+ handler: @escaping (NSDictionary) -> Void
35
+ ) {
36
+ onEvent = handler
37
+ }
38
+
39
+ @objc public func mount() {
40
+ NotificationCenter.default.addObserver(
41
+ self,
42
+ selector: #selector(keyboardWillShow),
43
+ name: UIResponder.keyboardWillShowNotification,
44
+ object: nil
45
+ )
46
+ NotificationCenter.default.addObserver(
47
+ self,
48
+ selector: #selector(keyboardWillHide),
49
+ name: UIResponder.keyboardWillHideNotification,
50
+ object: nil
51
+ )
52
+ }
53
+
54
+ @objc public func unmount() {
55
+ // swiftlint:disable:next notification_center_detachment
56
+ NotificationCenter.default.removeObserver(self)
57
+ }
58
+
59
+ @objc func keyboardWillShow(_: Notification) {
60
+ removeKVObserver()
61
+ currentInput = (UIResponder.current as? UIView)?.superview as UIView?
62
+ setupKVObserver()
63
+ syncUpLayout()
64
+ }
65
+
66
+ @objc func keyboardWillHide(_: Notification) {
67
+ removeKVObserver()
68
+ dispatchEventToJS(data: noFocusedInputEvent)
69
+ }
70
+
71
+ @objc func syncUpLayout() {
72
+ let responder = UIResponder.current
73
+ let focusedInput = (responder as? UIView)?.superview
74
+ let globalFrame = focusedInput?.globalFrame
75
+
76
+ let data: [String: Any] = [
77
+ "target": responder.reactViewTag,
78
+ "layout": [
79
+ "absoluteX": globalFrame?.origin.x,
80
+ "absoluteY": globalFrame?.origin.y,
81
+ "width": focusedInput?.frame.width,
82
+ "height": focusedInput?.frame.height,
83
+ "x": focusedInput?.frame.origin.x,
84
+ "y": focusedInput?.frame.origin.y,
85
+ ],
86
+ ]
87
+
88
+ dispatchEventToJS(data: data)
89
+ }
90
+
91
+ private func dispatchEventToJS(data: [String: Any]) {
92
+ if NSDictionary(dictionary: data).isEqual(to: lastEventDispatched) {
93
+ return
94
+ }
95
+
96
+ lastEventDispatched = data
97
+ onEvent(data as NSDictionary)
98
+ }
99
+
100
+ private func setupKVObserver() {
101
+ if hasKVObserver {
102
+ return
103
+ }
104
+
105
+ if currentInput != nil {
106
+ hasKVObserver = true
107
+ currentInput?.addObserver(self, forKeyPath: "center", options: .new, context: nil)
108
+ }
109
+ }
110
+
111
+ private func removeKVObserver() {
112
+ if !hasKVObserver {
113
+ return
114
+ }
115
+
116
+ hasKVObserver = false
117
+ currentInput?.removeObserver(self, forKeyPath: "center", context: nil)
118
+ }
119
+
120
+ // swiftlint:disable:next block_based_kvo
121
+ @objc override public func observeValue(
122
+ forKeyPath keyPath: String?,
123
+ of object: Any?,
124
+ change _: [NSKeyValueChangeKey: Any]?,
125
+ context _: UnsafeMutableRawPointer?
126
+ ) {
127
+ // swiftlint:disable:next force_cast
128
+ if keyPath == "center", object as! NSObject == currentInput! {
129
+ // we need to read layout in next frame, otherwise we'll get old
130
+ // layout values
131
+ DispatchQueue.main.async {
132
+ self.syncUpLayout()
133
+ }
134
+ }
135
+ }
136
+ }
@@ -9,6 +9,7 @@
9
9
  // This guard prevent the code from being compiled in the old architecture
10
10
  #ifdef RCT_NEW_ARCH_ENABLED
11
11
  #import "KeyboardControllerView.h"
12
+ #import "FocusedInputLayoutChangedEvent.h"
12
13
  #import "KeyboardMoveEvent.h"
13
14
  #import "react_native_keyboard_controller-Swift.h"
14
15
 
@@ -29,7 +30,8 @@ using namespace facebook::react;
29
30
  @end
30
31
 
31
32
  @implementation KeyboardControllerView {
32
- KeyboardMovementObserver *observer;
33
+ KeyboardMovementObserver *keyboardObserver;
34
+ FocusedInputLayoutObserver *inputObserver;
33
35
  }
34
36
 
35
37
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -43,7 +45,39 @@ using namespace facebook::react;
43
45
  static const auto defaultProps = std::make_shared<const KeyboardControllerViewProps>();
44
46
  _props = defaultProps;
45
47
 
46
- observer = [[KeyboardMovementObserver alloc]
48
+ inputObserver = [[FocusedInputLayoutObserver alloc] initWithHandler:^(NSDictionary *event) {
49
+ if (self->_eventEmitter) {
50
+ int target = [event[@"target"] integerValue];
51
+ double absoluteY = [event[@"layout"][@"absoluteY"] doubleValue];
52
+ double absoulteX = [event[@"layout"][@"absoluteX"] doubleValue];
53
+ double y = [event[@"layout"][@"y"] doubleValue];
54
+ double x = [event[@"layout"][@"x"] doubleValue];
55
+ double width = [event[@"layout"][@"width"] doubleValue];
56
+ double height = [event[@"layout"][@"height"] doubleValue];
57
+
58
+ std::dynamic_pointer_cast<const facebook::react::KeyboardControllerViewEventEmitter>(
59
+ self->_eventEmitter)
60
+ ->onFocusedInputLayoutChanged(
61
+ facebook::react::KeyboardControllerViewEventEmitter::OnFocusedInputLayoutChanged{
62
+ .target = target,
63
+ .layout = facebook::react::KeyboardControllerViewEventEmitter::
64
+ OnFocusedInputLayoutChangedLayout{
65
+ .absoluteY = absoluteY,
66
+ .absoluteX = absoulteX,
67
+ .height = height,
68
+ .width = width,
69
+ .x = x,
70
+ .y = y}});
71
+ // TODO: use built-in _eventEmitter once NativeAnimated module will use ModernEventemitter
72
+ RCTBridge *bridge = [RCTBridge currentBridge];
73
+ if (bridge && [bridge valueForKey:@"_jsThread"]) {
74
+ FocusedInputLayoutChangedEvent *inputChangedEvent =
75
+ [[FocusedInputLayoutChangedEvent alloc] initWithReactTag:@(self.tag) event:event];
76
+ [bridge.eventDispatcher sendEvent:inputChangedEvent];
77
+ }
78
+ }
79
+ }];
80
+ keyboardObserver = [[KeyboardMovementObserver alloc]
47
81
  initWithHandler:^(
48
82
  NSString *event,
49
83
  NSNumber *height,
@@ -110,12 +144,29 @@ using namespace facebook::react;
110
144
  onNotify:^(NSString *event, NSDictionary *data) {
111
145
  [KeyboardController.shared sendEvent:event body:data];
112
146
  }];
113
- [observer mount];
114
147
  }
115
148
 
116
149
  return self;
117
150
  }
118
151
 
152
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
153
+ {
154
+ const auto &oldViewProps = *std::static_pointer_cast<const KeyboardControllerViewProps>(_props);
155
+ const auto &newViewProps = *std::static_pointer_cast<const KeyboardControllerViewProps>(props);
156
+
157
+ if (newViewProps.enabled != oldViewProps.enabled) {
158
+ if (newViewProps.enabled) {
159
+ [inputObserver mount];
160
+ [keyboardObserver mount];
161
+ } else {
162
+ [inputObserver unmount];
163
+ [keyboardObserver unmount];
164
+ }
165
+ }
166
+
167
+ [super updateProps:props oldProps:oldProps];
168
+ }
169
+
119
170
  Class<RCTComponentViewProtocol> KeyboardControllerViewCls(void)
120
171
  {
121
172
  return KeyboardControllerView.class;
@@ -2,9 +2,16 @@
2
2
 
3
3
  @interface RCT_EXTERN_MODULE (KeyboardControllerViewManager, RCTViewManager)
4
4
 
5
+ // props
6
+ RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
7
+
8
+ // callbacks
9
+ /// keyboard callbacks
5
10
  RCT_EXPORT_VIEW_PROPERTY(onKeyboardMoveStart, RCTDirectEventBlock);
6
11
  RCT_EXPORT_VIEW_PROPERTY(onKeyboardMove, RCTDirectEventBlock);
7
12
  RCT_EXPORT_VIEW_PROPERTY(onKeyboardMoveEnd, RCTDirectEventBlock);
8
13
  RCT_EXPORT_VIEW_PROPERTY(onKeyboardMoveInteractive, RCTDirectEventBlock);
14
+ /// input callbacks
15
+ RCT_EXPORT_VIEW_PROPERTY(onFocusedInputLayoutChanged, RCTDirectEventBlock);
9
16
 
10
17
  @end
@@ -12,13 +12,26 @@ class KeyboardControllerViewManager: RCTViewManager {
12
12
  class KeyboardControllerView: UIView {
13
13
  // internal variables
14
14
  private var keyboardObserver: KeyboardMovementObserver?
15
+ private var inputObserver: FocusedInputLayoutObserver?
15
16
  private var eventDispatcher: RCTEventDispatcherProtocol
16
17
  private var bridge: RCTBridge
17
- // react props
18
+ // react callbacks
18
19
  @objc var onKeyboardMoveStart: RCTDirectEventBlock?
19
20
  @objc var onKeyboardMove: RCTDirectEventBlock?
20
21
  @objc var onKeyboardMoveEnd: RCTDirectEventBlock?
21
22
  @objc var onKeyboardMoveInteractive: RCTDirectEventBlock?
23
+ // react props
24
+ @objc var enabled: ObjCBool = true {
25
+ didSet {
26
+ if enabled.boolValue {
27
+ inputObserver?.mount()
28
+ keyboardObserver?.mount()
29
+ } else {
30
+ inputObserver?.unmount()
31
+ keyboardObserver?.unmount()
32
+ }
33
+ }
34
+ }
22
35
 
23
36
  init(frame: CGRect, bridge: RCTBridge) {
24
37
  self.bridge = bridge
@@ -34,6 +47,7 @@ class KeyboardControllerView: UIView {
34
47
  override func willMove(toWindow newWindow: UIWindow?) {
35
48
  if newWindow == nil {
36
49
  // Will be removed from a window
50
+ inputObserver?.unmount()
37
51
  keyboardObserver?.unmount()
38
52
  }
39
53
  }
@@ -41,11 +55,21 @@ class KeyboardControllerView: UIView {
41
55
  override func didMoveToWindow() {
42
56
  if window != nil {
43
57
  // Added to a window
58
+ inputObserver = FocusedInputLayoutObserver(handler: onInput)
59
+ inputObserver?.mount()
44
60
  keyboardObserver = KeyboardMovementObserver(handler: onEvent, onNotify: onNotify)
45
61
  keyboardObserver?.mount()
46
62
  }
47
63
  }
48
64
 
65
+ func onInput(event: NSObject) {
66
+ // we don't want to send event to JS before the JS thread is ready
67
+ if bridge.value(forKey: "_jsThread") == nil {
68
+ return
69
+ }
70
+ eventDispatcher.send(FocusedInputLayoutChangedEvent(reactTag: reactTag, event: event))
71
+ }
72
+
49
73
  func onEvent(event: NSString, height: NSNumber, progress: NSNumber, duration: NSNumber, target: NSNumber) {
50
74
  // we don't want to send event to JS before the JS thread is ready
51
75
  if bridge.value(forKey: "_jsThread") == nil {
package/jest/index.js CHANGED
@@ -10,6 +10,21 @@ const values = {
10
10
  height: { value: 0 },
11
11
  },
12
12
  };
13
+ const focusedInput = {
14
+ input: {
15
+ value: {
16
+ target: 1,
17
+ layout: {
18
+ x: 0,
19
+ y: 0,
20
+ width: 200,
21
+ height: 40,
22
+ absoluteX: 0,
23
+ absoluteY: 100,
24
+ },
25
+ },
26
+ },
27
+ };
13
28
 
14
29
  const mock = {
15
30
  // hooks
@@ -18,6 +33,7 @@ const mock = {
18
33
  useResizeMode: jest.fn(),
19
34
  useGenericKeyboardHandler: jest.fn(),
20
35
  useKeyboardHandler: jest.fn(),
36
+ useReanimatedFocusedInput: jest.fn().mockReturnValue(focusedInput),
21
37
  // modules
22
38
  KeyboardController: {
23
39
  setInputMode: jest.fn(),
@@ -7,9 +7,10 @@ exports.KeyboardProvider = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ var _bindings = require("./bindings");
10
11
  var _context = require("./context");
11
12
  var _internal = require("./internal");
12
- var _bindings = require("./bindings");
13
+ var _monkeyPatch = require("./monkey-patch");
13
14
  var _reanimated = require("./reanimated");
14
15
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
16
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -27,20 +28,24 @@ const KeyboardProvider = _ref => {
27
28
  let {
28
29
  children,
29
30
  statusBarTranslucent,
30
- navigationBarTranslucent
31
+ navigationBarTranslucent,
32
+ enabled: initiallyEnabled = true
31
33
  } = _ref;
34
+ const [enabled, setEnabled] = (0, _react.useState)(initiallyEnabled);
32
35
  // animated values
33
36
  const progress = (0, _internal.useAnimatedValue)(0);
34
37
  const height = (0, _internal.useAnimatedValue)(0);
35
38
  // shared values
36
39
  const progressSV = (0, _reactNativeReanimated.useSharedValue)(0);
37
40
  const heightSV = (0, _reactNativeReanimated.useSharedValue)(0);
41
+ const layout = (0, _reactNativeReanimated.useSharedValue)(null);
38
42
  const {
39
43
  setHandlers,
40
44
  broadcast
41
45
  } = (0, _internal.useSharedHandlers)();
42
46
  // memo
43
47
  const context = (0, _react.useMemo)(() => ({
48
+ enabled,
44
49
  animated: {
45
50
  progress: progress,
46
51
  height: _reactNative.Animated.multiply(height, -1)
@@ -49,8 +54,10 @@ const KeyboardProvider = _ref => {
49
54
  progress: progressSV,
50
55
  height: heightSV
51
56
  },
52
- setHandlers
53
- }), []);
57
+ layout,
58
+ setHandlers,
59
+ setEnabled
60
+ }), [enabled]);
54
61
  const style = (0, _react.useMemo)(() => [styles.hidden, {
55
62
  transform: [{
56
63
  translateX: height
@@ -75,7 +82,7 @@ const KeyboardProvider = _ref => {
75
82
  heightSV.value = -event.height;
76
83
  }
77
84
  };
78
- const handler = (0, _reanimated.useAnimatedKeyboardHandler)({
85
+ const keyboardHandler = (0, _reanimated.useAnimatedKeyboardHandler)({
79
86
  onKeyboardMoveStart: event => {
80
87
  'worklet';
81
88
 
@@ -100,13 +107,34 @@ const KeyboardProvider = _ref => {
100
107
  broadcast('onInteractive', event);
101
108
  }
102
109
  }, []);
110
+ const inputHandler = (0, _reanimated.useFocusedInputHandler)({
111
+ onFocusedInputLayoutChanged: e => {
112
+ 'worklet';
113
+
114
+ if (e.target !== -1) {
115
+ layout.value = e;
116
+ } else {
117
+ layout.value = null;
118
+ }
119
+ }
120
+ }, []);
121
+ // effects
122
+ (0, _react.useEffect)(() => {
123
+ if (enabled) {
124
+ (0, _monkeyPatch.applyMonkeyPatch)();
125
+ } else {
126
+ (0, _monkeyPatch.revertMonkeyPatch)();
127
+ }
128
+ }, [enabled]);
103
129
  return /*#__PURE__*/_react.default.createElement(_context.KeyboardContext.Provider, {
104
130
  value: context
105
131
  }, /*#__PURE__*/_react.default.createElement(KeyboardControllerViewAnimated, {
106
- onKeyboardMoveReanimated: handler,
132
+ enabled: enabled,
133
+ onKeyboardMoveReanimated: keyboardHandler,
107
134
  onKeyboardMoveStart: _reactNative.Platform.OS === 'ios' ? onKeyboardMove : undefined,
108
135
  onKeyboardMove: _reactNative.Platform.OS === 'android' ? onKeyboardMove : undefined,
109
136
  onKeyboardMoveInteractive: onKeyboardMove,
137
+ onFocusedInputLayoutChangedReanimated: inputHandler,
110
138
  navigationBarTranslucent: navigationBarTranslucent,
111
139
  statusBarTranslucent: statusBarTranslucent
112
140
  // @ts-expect-error https://github.com/software-mansion/react-native-reanimated/pull/4923