react-native-external-keyboard 0.6.1 → 0.6.2
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 +11 -11
- package/android/src/main/java/com/externalkeyboard/views/TextInputFocusWrapper/TextInputFocusWrapper.java +22 -16
- package/ios/Delegates/RNCEKVHaloDelegate/RNCEKVHaloDelegate.mm +26 -24
- package/ios/Extensions/RCTEnhancedScrollView+RNCEKVExternalKeyboard.mm +24 -0
- package/ios/Views/RNCEKVExternalKeyboardView/RNCEKVExternalKeyboardView.mm +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ React Native library for enhanced external keyboard support.
|
|
|
6
6
|
- ⚡️ Bridgeless
|
|
7
7
|
|
|
8
8
|
> [!NOTE]
|
|
9
|
-
> React Native `0.80.0` includes a fix for `TextInput` focus on Android.
|
|
9
|
+
> React Native `0.80.0` (`0.79.0`) includes a fix for `TextInput` focus on Android.
|
|
10
10
|
> `TextInput`: Can now focus TextInput with keyboard ([e00028f6bb](https://github.com/facebook/react-native/commit/e00028f6bb6c19de861f9a25f377295755f3671b) by [@joevilches](https://github.com/joevilches))
|
|
11
11
|
>
|
|
12
12
|
> This means that there is no need to use the workaround with `KeyboardExtendedInput`, and it is recommended to use the default `TextInput` instead. Additionally, because of changes in `TextInput`, `focusType="press"` for `KeyboardExtendedInput` no longer works on Android.
|
|
@@ -21,10 +21,10 @@ Improved keyboard focus control with features like focus order, ordered links, a
|
|
|
21
21
|
|
|
22
22
|
<details>
|
|
23
23
|
<summary>More Information</summary>
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
Advanced focus order functionality for Android and iOS, plus focus lock!
|
|
26
26
|
|
|
27
|
-
It can be really challenging to manage focus in React Native, but fortunately, there are tools available to simplify the process.
|
|
27
|
+
It can be really challenging to manage focus in React Native, but fortunately, there are tools available to simplify the process.
|
|
28
28
|
|
|
29
29
|
## Link Focus Order
|
|
30
30
|
|
|
@@ -119,7 +119,7 @@ You can find more examples here: [Focus Order via indexes](https://github.com/Ar
|
|
|
119
119
|
|
|
120
120
|
## Focus Lock
|
|
121
121
|
|
|
122
|
-
Finally, you can lock focus to specific directions.
|
|
122
|
+
Finally, you can lock focus to specific directions.
|
|
123
123
|
|
|
124
124
|
```tsx
|
|
125
125
|
<Pressable
|
|
@@ -134,7 +134,7 @@ Finally, you can lock focus to specific directions.
|
|
|
134
134
|
| :-- | :-- | :-- |
|
|
135
135
|
| lockFocus? | An array of directions to lock focus. | Array of 'left' | 'right' | 'up' | 'down' | 'forward' | 'backward' | 'first' | 'last' |
|
|
136
136
|
|
|
137
|
-
> [!NOTE]
|
|
137
|
+
> [!NOTE]
|
|
138
138
|
> `first` and `last` are specific to `iOS`. When focus is blocked for `forward` and `backward` on iOS, it checks for the `last` and `first` elements to focus.
|
|
139
139
|
</details>
|
|
140
140
|
|
|
@@ -191,7 +191,7 @@ const KeybardedButton = withKeyboardFocus(Button);
|
|
|
191
191
|
</TouchableOpacity>
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
After wrapping a `Pressable` or `Touchable` with `withKeyboardFocus`, you will be able to handle `focus` and `blur` events, control the `tint color`, apply focus and container focus styles, `focus` the component using a `ref`, or configure `autoFocus`.
|
|
194
|
+
After wrapping a `Pressable` or `Touchable` with `withKeyboardFocus`, you will be able to handle `focus` and `blur` events, control the `tint color`, apply focus and container focus styles, `focus` the component using a `ref`, or configure `autoFocus`.
|
|
195
195
|
|
|
196
196
|
|
|
197
197
|
Props | Description | Type
|
|
@@ -372,7 +372,7 @@ lockFocus? | An array of directions to lock focus. | Array of 'left' \| 'right'
|
|
|
372
372
|
The `KeyboardFocusGroup` is a View-based component developed based on the iOS API. It can be used for defining focus groups or setting the `tintColor` globally.
|
|
373
373
|
|
|
374
374
|
```tsx
|
|
375
|
-
<KeyboardFocusGroup
|
|
375
|
+
<KeyboardFocusGroup
|
|
376
376
|
tintColor="orange">
|
|
377
377
|
<ScrollView
|
|
378
378
|
contentContainerStyle={styles.contentContainer}
|
|
@@ -381,7 +381,7 @@ The `KeyboardFocusGroup` is a View-based component developed based on the iOS AP
|
|
|
381
381
|
...
|
|
382
382
|
</ScrollView>
|
|
383
383
|
</KeyboardFocusGroup>
|
|
384
|
-
<KeyboardFocusGroup
|
|
384
|
+
<KeyboardFocusGroup
|
|
385
385
|
focusStyle={{ backgroundColor: 'green' }}
|
|
386
386
|
onFocusChange={(e) => console.log('green', e)}
|
|
387
387
|
groupIdentifier="green"
|
|
@@ -515,7 +515,7 @@ You can find more examples here: [Focus Order via indexes](https://github.com/Ar
|
|
|
515
515
|
|
|
516
516
|
## Focus Lock
|
|
517
517
|
|
|
518
|
-
Finally, you can lock focus to specific directions.
|
|
518
|
+
Finally, you can lock focus to specific directions.
|
|
519
519
|
|
|
520
520
|
```tsx
|
|
521
521
|
<Pressable
|
|
@@ -530,7 +530,7 @@ Finally, you can lock focus to specific directions.
|
|
|
530
530
|
| :-- | :-- | :-- |
|
|
531
531
|
| lockFocus? | An array of directions to lock focus. | Array of 'left' \| 'right' \| 'up' \| 'down' \| 'forward' \| 'backward' \| 'first' \| 'last' |
|
|
532
532
|
|
|
533
|
-
> [!NOTE]
|
|
533
|
+
> [!NOTE]
|
|
534
534
|
> `first` and `last` are specific to `iOS`. When focus is blocked for `forward` and `backward` on iOS, it checks for the `last` and `first` elements to focus.
|
|
535
535
|
|
|
536
536
|
|
|
@@ -547,7 +547,7 @@ import { KeyboardExtendedModule } from 'react-native-external-keyboard';
|
|
|
547
547
|
KeyboardExtendedModule.setKeyboardFocus(ref); //or A11yModule.setKeyboardFocus(ref);
|
|
548
548
|
```
|
|
549
549
|
|
|
550
|
-
Updated:
|
|
550
|
+
Updated:
|
|
551
551
|
```
|
|
552
552
|
import { KeyboardExtendedPressable, type KeyboardFocus } from 'react-native-external-keyboard';
|
|
553
553
|
...
|
|
@@ -14,6 +14,7 @@ import com.externalkeyboard.events.EventHelper;
|
|
|
14
14
|
import com.externalkeyboard.helper.ReactNativeVersionChecker;
|
|
15
15
|
import com.externalkeyboard.modules.ExternalKeyboardModule;
|
|
16
16
|
import com.facebook.react.bridge.ReactContext;
|
|
17
|
+
import com.facebook.react.modules.systeminfo.ReactNativeVersion;
|
|
17
18
|
import com.facebook.react.views.textinput.ReactEditText;
|
|
18
19
|
|
|
19
20
|
import java.lang.reflect.Field;
|
|
@@ -32,9 +33,14 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
32
33
|
public boolean getIsFocusByPress() {
|
|
33
34
|
return focusType == FOCUS_BY_PRESS;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
private boolean getIsNativelyFixedVersion () {
|
|
37
|
+
try {
|
|
38
|
+
Object minorValue = ReactNativeVersion.VERSION.getOrDefault("minor", 0);
|
|
39
|
+
int minor = (minorValue instanceof Integer) ? (int) minorValue : 0;
|
|
40
|
+
return minor >= 79;
|
|
41
|
+
} catch (Exception e) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
public void setKeyboardFocusable(boolean canBeFocusable) {
|
|
@@ -46,8 +52,8 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
46
52
|
|
|
47
53
|
this.setFocusable(keyboardFocusable);
|
|
48
54
|
if (this.reactEditText != null) {
|
|
49
|
-
boolean
|
|
50
|
-
this.reactEditText.setFocusable(
|
|
55
|
+
boolean isAlreadyFixed = getIsNativelyFixedVersion();
|
|
56
|
+
this.reactEditText.setFocusable(isAlreadyFixed);
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
|
|
@@ -56,8 +62,8 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
56
62
|
onAttachListener = new View.OnAttachStateChangeListener() {
|
|
57
63
|
@Override
|
|
58
64
|
public void onViewAttachedToWindow(@NonNull View view) {
|
|
59
|
-
boolean
|
|
60
|
-
view.setFocusable(
|
|
65
|
+
boolean isAlreadyFixed = getIsNativelyFixedVersion();
|
|
66
|
+
view.setFocusable(isAlreadyFixed);
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
@Override
|
|
@@ -83,14 +89,14 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
83
89
|
public void setEditText(ReactEditText editText) {
|
|
84
90
|
if (editText != null) {
|
|
85
91
|
this.reactEditText = editText;
|
|
86
|
-
boolean
|
|
87
|
-
if(
|
|
92
|
+
boolean isAlreadyFixed = getIsNativelyFixedVersion();
|
|
93
|
+
if(isAlreadyFixed) {
|
|
88
94
|
this.setFocusable(false);
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
this.reactEditText.addOnAttachStateChangeListener(getOnAttachListener());
|
|
92
98
|
if (focusType == FOCUS_BY_PRESS) {
|
|
93
|
-
this.reactEditText.setFocusable(
|
|
99
|
+
this.reactEditText.setFocusable(isAlreadyFixed);
|
|
94
100
|
}
|
|
95
101
|
OnFocusChangeListener reactListener = this.reactEditText.getOnFocusChangeListener();
|
|
96
102
|
this.reactEditText.setOnFocusChangeListener((textInput, hasTextEditFocus) -> {
|
|
@@ -105,8 +111,8 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
105
111
|
ExternalKeyboardModule.setFocusedTextInput(textInput);
|
|
106
112
|
}
|
|
107
113
|
if (!hasTextEditFocus) {
|
|
108
|
-
this.setFocusable(!
|
|
109
|
-
this.reactEditText.setFocusable(
|
|
114
|
+
this.setFocusable(!isAlreadyFixed);
|
|
115
|
+
this.reactEditText.setFocusable(isAlreadyFixed);
|
|
110
116
|
}
|
|
111
117
|
});
|
|
112
118
|
onMultiplyBlurSubmitHandle();
|
|
@@ -139,8 +145,8 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
139
145
|
this.context = context;
|
|
140
146
|
|
|
141
147
|
if (keyboardFocusable) {
|
|
142
|
-
boolean
|
|
143
|
-
setFocusable(!
|
|
148
|
+
boolean isAlreadyFixed = getIsNativelyFixedVersion();
|
|
149
|
+
setFocusable(!isAlreadyFixed);
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
152
|
|
|
@@ -166,8 +172,8 @@ public class TextInputFocusWrapper extends ViewGroup implements View.OnFocusChan
|
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
|
|
169
|
-
boolean
|
|
170
|
-
if(
|
|
175
|
+
boolean isAlreadyFixed = getIsNativelyFixedVersion();
|
|
176
|
+
if(isAlreadyFixed) {
|
|
171
177
|
return super.requestFocus(direction, previouslyFocusedRect);
|
|
172
178
|
}
|
|
173
179
|
if ((direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) && focusType != FOCUS_BY_PRESS) {
|
|
@@ -51,38 +51,38 @@
|
|
|
51
51
|
if (@available(iOS 15.0, *)) {
|
|
52
52
|
UIView *focusingView = [_delegate getFocusTargetView];
|
|
53
53
|
UIFocusEffect *prevEffect = _focusEffect;
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
BOOL isHidden = [self isHaloHidden];
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
if (isHidden) {
|
|
58
58
|
_focusEffect = [RNCEKVFocusEffectUtility emptyFocusEffect];
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
BOOL hasHaloSettings = _delegate.haloExpendX || _delegate.haloExpendY ||
|
|
62
|
-
|
|
62
|
+
_delegate.haloCornerRadius;
|
|
63
63
|
BOOL isDifferentBounds =
|
|
64
|
-
|
|
64
|
+
!CGRectEqualToRect(_prevBounds, focusingView.bounds);
|
|
65
65
|
BOOL isDifferent = _prevHaloExpendX != _delegate.haloExpendX ||
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
_prevHaloExpendY != _delegate.haloExpendY ||
|
|
67
|
+
_prevHaloCornerRadius != _delegate.haloCornerRadius ||
|
|
68
|
+
isDifferentBounds;
|
|
69
|
+
|
|
70
70
|
// ToDo refactor for better halo setup RNCEKV-7, RNCEKV-8
|
|
71
71
|
if (!isHidden && hasHaloSettings && isDifferent) {
|
|
72
72
|
_prevHaloExpendX = _delegate.haloExpendX;
|
|
73
73
|
_prevHaloExpendY = _delegate.haloExpendY;
|
|
74
74
|
_prevHaloCornerRadius = _delegate.haloCornerRadius;
|
|
75
75
|
_prevBounds = focusingView.bounds;
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
_focusEffect =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
[RNCEKVFocusEffectUtility getFocusEffect:focusingView
|
|
79
|
+
withExpandedX:_delegate.haloExpendX
|
|
80
|
+
withExpandedY:_delegate.haloExpendY
|
|
81
|
+
withCornerRadius:_delegate.haloCornerRadius];
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
if ((_focusEffect == nil && _recycled) || (_focusEffect != nil && prevEffect != _focusEffect &&
|
|
85
|
-
|
|
85
|
+
focusingView.focusEffect != _focusEffect)) {
|
|
86
86
|
_recycled = false;
|
|
87
87
|
focusingView.focusEffect = _focusEffect;
|
|
88
88
|
}
|
|
@@ -94,24 +94,26 @@
|
|
|
94
94
|
return;
|
|
95
95
|
if (@available(iOS 15.0, *)) {
|
|
96
96
|
BOOL shouldUpdate = _delegate.haloExpendX || _delegate.haloExpendY ||
|
|
97
|
-
|
|
97
|
+
_delegate.haloCornerRadius;
|
|
98
98
|
if (!shouldUpdate)
|
|
99
99
|
return;
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
UIView *focusingView = [_delegate getFocusTargetView];
|
|
102
102
|
UIFocusEffect *focusEffect =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
[RNCEKVFocusEffectUtility getFocusEffect:focusingView
|
|
104
|
+
withExpandedX:_delegate.haloExpendX
|
|
105
|
+
withExpandedY:_delegate.haloExpendY
|
|
106
|
+
withCornerRadius:_delegate.haloCornerRadius];
|
|
107
|
+
|
|
108
108
|
focusingView.focusEffect = focusEffect;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
- (void) clear {
|
|
113
113
|
UIView *focusingView = [_delegate getFocusTargetView];
|
|
114
|
-
|
|
114
|
+
if (@available(iOS 15.0, *)) {
|
|
115
|
+
focusingView.focusEffect = nil;
|
|
116
|
+
}
|
|
115
117
|
_focusEffect = nil;
|
|
116
118
|
_recycled = true;
|
|
117
119
|
_prevBounds = CGRect();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RCTEnhancedScrollView+RNCEKVExternalKeyboard.mm
|
|
3
|
+
// react-native-external-keyboard
|
|
4
|
+
//
|
|
5
|
+
// Created by Artur Kalach on 09/08/2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
10
|
+
|
|
11
|
+
#import "RCTEnhancedScrollView.h"
|
|
12
|
+
|
|
13
|
+
@implementation RCTEnhancedScrollView (RNCEKVExternalKeyboard)
|
|
14
|
+
|
|
15
|
+
- (NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments {
|
|
16
|
+
if(self.subviews.count) {
|
|
17
|
+
return @[self.subviews[0]];
|
|
18
|
+
}
|
|
19
|
+
return [super preferredFocusEnvironments];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
#endif
|
|
@@ -157,6 +157,10 @@ using namespace facebook::react;
|
|
|
157
157
|
_orderRight = nil;
|
|
158
158
|
_orderUp = nil;
|
|
159
159
|
_orderDown = nil;
|
|
160
|
+
_orderForward = nil;
|
|
161
|
+
_orderBackward = nil;
|
|
162
|
+
_orderLast = nil;
|
|
163
|
+
_orderFirst = nil;
|
|
160
164
|
_orderId = nil;
|
|
161
165
|
_lockFocus = nil;
|
|
162
166
|
_customGroupId = nil;
|
|
@@ -559,6 +563,9 @@ Class<RCTComponentViewProtocol> ExternalKeyboardViewCls(void) {
|
|
|
559
563
|
|
|
560
564
|
- (void)subviewRecycle: (UIView *)subview {
|
|
561
565
|
[_gIdDelegate clearSubview: subview];
|
|
566
|
+
if (@available(iOS 15.0, *)) {
|
|
567
|
+
subview.focusEffect = nil;
|
|
568
|
+
}
|
|
562
569
|
}
|
|
563
570
|
|
|
564
571
|
- (void)willRemoveSubview:(UIView *)subview {
|