react-native-external-keyboard 0.6.7 → 0.7.0-rc
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.
Potentially problematic release.
This version of react-native-external-keyboard might be problematic. Click here for more details.
- package/README.md +38 -131
- package/android/src/main/java/com/externalkeyboard/ExternalKeyboardViewPackage.java +2 -0
- package/android/src/main/java/com/externalkeyboard/helper/FocusHelper.java +70 -0
- package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardLockView/ExternalKeyboardLockView.java +115 -0
- package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardLockView/ExternalKeyboardLockViewManager.java +32 -0
- package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardLockView/LockService.java +44 -0
- package/android/src/newarch/ExternalKeyboardLockViewManagerSpec.java +8 -0
- package/android/src/oldarch/ExternalKeyboardLockViewManagerSpec.java +12 -0
- package/ios/Delegates/RNCEKVFocusDelegate/RNCEKVFocusDelegate.mm +58 -21
- package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusGuideDelegate/RNCEKVFocusGuideDelegate.h +36 -0
- package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusGuideDelegate/RNCEKVFocusGuideDelegate.mm +150 -0
- package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusOrderDelegate.h +14 -0
- package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusOrderDelegate.mm +98 -349
- package/ios/Extensions/RCTEnhancedScrollView+RNCEKVExternalKeyboard.mm +12 -2
- package/ios/Extensions/RCTTextInputComponentView+RNCEKVExternalKeyboard.h +1 -1
- package/ios/Extensions/RCTTextInputComponentView+RNCEKVExternalKeyboard.mm +11 -3
- package/ios/Extensions/UIViewController+RNCEKVExternalKeyboard.h +1 -1
- package/ios/Extensions/UIViewController+RNCEKVExternalKeyboard.mm +17 -17
- package/ios/Helpers/RNCEKVFocusGuideHelper/RNCEKVFocusGuideHelper.h +32 -0
- package/ios/Helpers/RNCEKVFocusGuideHelper/RNCEKVFocusGuideHelper.mm +81 -0
- package/ios/Helpers/RNCEKVSwizzlingHelper/RNCEKVSwizzlingHelper.h +16 -0
- package/ios/Helpers/RNCEKVSwizzlingHelper/RNCEKVSwizzlingHelper.mm +27 -0
- package/ios/Modules/RNCEKVExternalKeyboardModule.mm +3 -3
- package/ios/Services/RNCEKVFocusLinkObserver.h +7 -1
- package/ios/Services/RNCEKVFocusLinkObserver.mm +31 -6
- package/ios/Services/RNCEKVKeyboardOrderManager/RNCEKVKeyboardOrderManager.h +17 -0
- package/ios/Services/RNCEKVKeyboardOrderManager/RNCEKVKeyboardOrderManager.mm +15 -0
- package/ios/Services/{RNCEKVRelashioship.h → RNCEKVKeyboardOrderManager/RNCEKVOrderRelationship/RNCEKVOrderRelationship.h} +8 -7
- package/ios/Services/{RNCEKVRelashioship.mm → RNCEKVKeyboardOrderManager/RNCEKVOrderRelationship/RNCEKVOrderRelationship.mm} +36 -13
- package/ios/Services/RNCEKVOrderLinking.h +2 -7
- package/ios/Services/RNCEKVOrderLinking.mm +27 -64
- package/ios/Services/RNCEKVOrderSubscriber.h +4 -3
- package/ios/Services/RNCEKVOrderSubscriber.mm +2 -1
- package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockView.h +38 -0
- package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockView.mm +62 -0
- package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockViewManager.h +14 -0
- package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockViewManager.mm +25 -0
- package/ios/Views/RNCEKVExternalKeyboardView/RNCEKVExternalKeyboardView.mm +2 -2
- package/ios/Views/RNCEKVKeyboardFocusGroupView/RNCEKVKeyboardFocusGroup.mm +0 -1
- package/ios/Views/RNCEKVTextInputFocusWrapper/RNCEKVTextInputFocusWrapper.mm +1 -15
- package/lib/commonjs/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.js +21 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.js.map +1 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusFrame/FocusFrame.js +11 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusFrame/FocusFrame.js.map +1 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.js +19 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.js.map +1 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrap.js +15 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrap.js.map +1 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.js +46 -0
- package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.js.map +1 -0
- package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js +21 -0
- package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js.map +1 -0
- package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js +9 -0
- package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js.map +1 -0
- package/lib/commonjs/context/FocusFrameProviderContext.js +28 -0
- package/lib/commonjs/context/FocusFrameProviderContext.js.map +1 -0
- package/lib/commonjs/index.js +7 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/nativeSpec/ExternalKeyboardLockViewNativeComponent.js +10 -0
- package/lib/commonjs/nativeSpec/ExternalKeyboardLockViewNativeComponent.js.map +1 -0
- package/lib/commonjs/types/KeyboardFocusLock.types.js +6 -0
- package/lib/commonjs/types/KeyboardFocusLock.types.js.map +1 -0
- package/lib/commonjs/utils/useKeyboardPress/useKeyboardPress.android.js +25 -12
- package/lib/commonjs/utils/useKeyboardPress/useKeyboardPress.android.js.map +1 -1
- package/lib/module/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.js +14 -0
- package/lib/module/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.js.map +1 -0
- package/lib/module/components/KeyboardFocusLock/FocusFrame/FocusFrame.js +4 -0
- package/lib/module/components/KeyboardFocusLock/FocusFrame/FocusFrame.js.map +1 -0
- package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.js +12 -0
- package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.js.map +1 -0
- package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrap.js +8 -0
- package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrap.js.map +1 -0
- package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.js +39 -0
- package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.js.map +1 -0
- package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js +14 -0
- package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js.map +1 -0
- package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js +3 -0
- package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js.map +1 -0
- package/lib/module/context/FocusFrameProviderContext.js +20 -0
- package/lib/module/context/FocusFrameProviderContext.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/nativeSpec/ExternalKeyboardLockViewNativeComponent.js +3 -0
- package/lib/module/nativeSpec/ExternalKeyboardLockViewNativeComponent.js.map +1 -0
- package/lib/module/types/KeyboardFocusLock.types.js +2 -0
- package/lib/module/types/KeyboardFocusLock.types.js.map +1 -0
- package/lib/module/utils/useKeyboardPress/useKeyboardPress.android.js +25 -12
- package/lib/module/utils/useKeyboardPress/useKeyboardPress.android.js.map +1 -1
- package/lib/typescript/src/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.d.ts +4 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.d.ts.map +1 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusFrame/FocusFrame.d.ts +3 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusFrame/FocusFrame.d.ts.map +1 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.d.ts +4 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.d.ts.map +1 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.d.ts +3 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.d.ts.map +1 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.d.ts +3 -0
- package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.d.ts.map +1 -0
- package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.d.ts +4 -0
- package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.d.ts.map +1 -0
- package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.d.ts +3 -0
- package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.d.ts.map +1 -0
- package/lib/typescript/src/components/Touchable/Pressable.d.ts +1 -1
- package/lib/typescript/src/context/FocusFrameProviderContext.d.ts +12 -0
- package/lib/typescript/src/context/FocusFrameProviderContext.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/nativeSpec/ExternalKeyboardLockViewNativeComponent.d.ts +9 -0
- package/lib/typescript/src/nativeSpec/ExternalKeyboardLockViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/types/KeyboardFocusLock.types.d.ts +6 -0
- package/lib/typescript/src/types/KeyboardFocusLock.types.d.ts.map +1 -0
- package/lib/typescript/src/utils/useKeyboardPress/useKeyboardPress.android.d.ts.map +1 -1
- package/lib/typescript/src/utils/withKeyboardFocus.d.ts +1 -1
- package/package.json +6 -5
- package/src/components/KeyboardFocusLock/FocusFrame/FocusFrame.android.tsx +18 -0
- package/src/components/KeyboardFocusLock/FocusFrame/FocusFrame.tsx +8 -0
- package/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.android.tsx +17 -0
- package/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.tsx +9 -0
- package/src/components/KeyboardFocusLock/FocusTrap/FocusTrapMountWrapper.ts +40 -0
- package/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.tsx +16 -0
- package/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.tsx +5 -0
- package/src/context/FocusFrameProviderContext.tsx +36 -0
- package/src/index.tsx +8 -0
- package/src/nativeSpec/ExternalKeyboardLockViewNativeComponent.ts +13 -0
- package/src/types/KeyboardFocusLock.types.ts +6 -0
- package/src/utils/useKeyboardPress/useKeyboardPress.android.ts +36 -12
- package/ios/Services/RNCEKVFocusLinkObserverManager.h +0 -23
- package/ios/Services/RNCEKVFocusLinkObserverManager.mm +0 -30
- /package/ios/Services/{RNCEKVSortedMap.h → RNCEKVKeyboardOrderManager/RNCEKVOrderRelationship/RNCEKVSortedMap/RNCEKVSortedMap.h} +0 -0
- /package/ios/Services/{RNCEKVSortedMap.mm → RNCEKVKeyboardOrderManager/RNCEKVOrderRelationship/RNCEKVSortedMap/RNCEKVSortedMap.mm} +0 -0
package/README.md
CHANGED
|
@@ -5,152 +5,59 @@ React Native library for enhanced external keyboard support.
|
|
|
5
5
|
- ⚡️ The New Architecture is supported
|
|
6
6
|
- ⚡️ Bridgeless
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
>
|
|
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.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## New Release: Focus Lock and Focus Order
|
|
16
|
-
Improved keyboard focus control with features like focus order, ordered links, and focus lock.
|
|
17
|
-
|
|
18
|
-
|Android| iOS|
|
|
19
|
-
|-|-|
|
|
20
|
-
| <image alt="Android Focus Order Example" src="https://github.com/user-attachments/assets/8bae353e-cf4b-41f7-8796-fd1cceae5df5" height="400" />| <image alt="iOS Focus Order Example" src="https://github.com/user-attachments/assets/51850cc6-9573-4ccb-b69c-14deefbb4b65" height="400"/> |
|
|
21
|
-
|
|
22
|
-
<details>
|
|
23
|
-
<summary>More Information</summary>
|
|
24
|
-
|
|
25
|
-
Advanced focus order functionality for Android and iOS, plus focus lock!
|
|
26
|
-
|
|
27
|
-
It can be really challenging to manage focus in React Native, but fortunately, there are tools available to simplify the process.
|
|
28
|
-
|
|
29
|
-
## Link Focus Order
|
|
30
|
-
|
|
31
|
-
`Linking` components could be the most logical way to define focus order. By using properties such as `orderId` and `orderBackward`, `orderForward`, `orderLeft`, `orderRight`, `orderUp`, and `orderDown`, you can customize the focus order according to your needs.
|
|
8
|
+
iOS | Android
|
|
9
|
+
-- | --
|
|
10
|
+
<img src="/.github/images/rnek-ios-example.gif" height="500" /> | <img src="/.github/images/rnek-android-example.gif" height="500" />
|
|
32
11
|
|
|
33
|
-
|
|
34
|
-
<View>
|
|
35
|
-
<Pressable
|
|
36
|
-
onPress={onPress}
|
|
37
|
-
orderId="0_0"
|
|
38
|
-
orderForward="0_2"
|
|
39
|
-
>
|
|
40
|
-
<Text>1</Text>
|
|
41
|
-
</Pressable>
|
|
42
|
-
<Pressable
|
|
43
|
-
onPress={onPress}
|
|
44
|
-
orderId="0_2"
|
|
45
|
-
orderBackward="0_1"
|
|
46
|
-
>
|
|
47
|
-
<Text>3</Text>
|
|
48
|
-
</Pressable>
|
|
49
|
-
<Pressable
|
|
50
|
-
onPress={onPress}
|
|
51
|
-
orderId="0_1"
|
|
52
|
-
orderForward="0_2"
|
|
53
|
-
orderBackward="0_0"
|
|
54
|
-
>
|
|
55
|
-
<Text>2</Text>
|
|
56
|
-
</Pressable>
|
|
57
|
-
</View>
|
|
58
|
-
```
|
|
12
|
+
## Features
|
|
59
13
|
|
|
60
|
-
|
|
14
|
+
- Keyboard focus management and autofocus capabilities.
|
|
15
|
+
- Key press event handling.
|
|
16
|
+
- Focus management for `TextInput` and `Pressable` components.
|
|
17
|
+
- Customization of the `Halo Effect` and `tintColor` for iOS.
|
|
18
|
+
- Keyboard focus order.
|
|
61
19
|
|
|
62
|
-
|
|
63
|
-
| :-- | :-- | :-- |
|
|
64
|
-
| orderId? | A unique ID used for link target identification. | `string` |
|
|
65
|
-
| orderBackward? | ID of the target for backward navigation with "Tab + Shift". | `string` |
|
|
66
|
-
| orderForward? | ID of the target for forward navigation with "Tab". | `string` |
|
|
67
|
-
| orderLeft? | ID of the target for navigation to the left. | `string` |
|
|
68
|
-
| orderRight? | ID of the target for navigation to the right. | `string` |
|
|
69
|
-
| orderUp? | ID of the target for navigation upward. | `string` |
|
|
70
|
-
| orderDown? | ID of the target for navigation downward. | `string` |
|
|
20
|
+
## New Release: Focus Lock
|
|
71
21
|
|
|
72
|
-
|
|
22
|
+
| iOS | Android |
|
|
23
|
+
| :-- | :-- |
|
|
24
|
+
| <img src="/.github/images/rnek-focus-lock-ios.gif" height="500" /> | <img src="/.github/images/rnek-focus-lock-ios.gif" height="500" /> |
|
|
73
25
|
|
|
74
|
-
|
|
26
|
+
> A new type of focus lock functionality has been introduced, featuring two new components: `Focus.Frame` and `Focus.Trap`. These components help manage and lock focus within specific areas of the screen.
|
|
75
27
|
|
|
76
|
-
|
|
77
|
-
<
|
|
78
|
-
<View>
|
|
79
|
-
<Pressable
|
|
80
|
-
onPress={onPress}
|
|
81
|
-
orderIndex={0}
|
|
82
|
-
>
|
|
83
|
-
<Text>First</Text>
|
|
84
|
-
</Pressable>
|
|
85
|
-
<Pressable
|
|
86
|
-
onPress={onPress}
|
|
87
|
-
orderIndex={2}
|
|
88
|
-
>
|
|
89
|
-
<Text>Third</Text>
|
|
90
|
-
</Pressable>
|
|
91
|
-
<Pressable
|
|
92
|
-
onPress={onPress}
|
|
93
|
-
orderIndex={1}
|
|
94
|
-
>
|
|
95
|
-
<Text>Second</Text>
|
|
96
|
-
</Pressable>
|
|
97
|
-
</View>
|
|
98
|
-
</KeyboardOrderFocusGroup>
|
|
99
|
-
```
|
|
100
|
-
Indexing requres `orderGroup` param for proper order set, you can use `KeyboardOrderFocusGroup` or provide `orderGroup` to the component.
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
<Pressable
|
|
104
|
-
orderGroup="main"
|
|
105
|
-
onPress={onPress}
|
|
106
|
-
orderIndex={2}
|
|
107
|
-
>
|
|
108
|
-
<Text>Back</Text>
|
|
109
|
-
</Pressable>
|
|
110
|
-
```
|
|
28
|
+
<details>
|
|
29
|
+
<summary>More Information</summary>
|
|
111
30
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
| orderGroup? | The name of the group containing ordered elements. | `string` |
|
|
115
|
-
| orderIndex? | The order index of the element within the group. | `number` |
|
|
31
|
+
- On iOS, `Focus.Trap` uses the native `accessibilityViewIsModal` property to keep the focus within a defined area.
|
|
32
|
+
- On Android, where no equivalent to `accessibilityViewIsModal` exists, custom logic has been implemented as a workaround. By default, Android uses a custom Activity or Modal to limit focus. While using a Modal is considered the best practice for focus locking on Android, some scenarios—such as issues with React Native's Modal or library-specific constraints—may require alternative implementations.
|
|
116
33
|
|
|
34
|
+
#### How It Works
|
|
117
35
|
|
|
118
|
-
|
|
36
|
+
The focus lock functionality should be used as a pair:
|
|
119
37
|
|
|
120
|
-
|
|
38
|
+
- `Focus.Frame`: This component is used at the root level of a "screen" to detect focus leaks and ensure that focus remains contained.
|
|
39
|
+
- `Focus.Trap`: This component wraps the content area where focus should be explicitly locked.
|
|
121
40
|
|
|
122
|
-
|
|
41
|
+
| Prop | Description |
|
|
42
|
+
| :-- | :-- |
|
|
43
|
+
| ViewProps | Includes all standard React Native View properties, such as style, testID, etc. |
|
|
123
44
|
|
|
124
45
|
```tsx
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
>
|
|
128
|
-
|
|
129
|
-
|
|
46
|
+
<Focus.Frame>
|
|
47
|
+
...
|
|
48
|
+
<Focus.Trap>
|
|
49
|
+
<Text accessibilityRole="header">Locked Area</Text>
|
|
50
|
+
<Button
|
|
51
|
+
title="Confirm"
|
|
52
|
+
accessibilityLabel="Confirm action"
|
|
53
|
+
/>
|
|
54
|
+
</Focus.Trap>
|
|
55
|
+
...
|
|
56
|
+
</Focus.Frame>
|
|
130
57
|
```
|
|
131
58
|
|
|
132
|
-
|
|
133
|
-
| Props | Description | Type |
|
|
134
|
-
| :-- | :-- | :-- |
|
|
135
|
-
| lockFocus? | An array of directions to lock focus. | Array of 'left' | 'right' | 'up' | 'down' | 'forward' | 'backward' | 'first' | 'last' |
|
|
136
|
-
|
|
137
|
-
> [!NOTE]
|
|
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
59
|
</details>
|
|
140
60
|
|
|
141
|
-
|
|
142
|
-
iOS | Android
|
|
143
|
-
-- | --
|
|
144
|
-
<img src="/.github/images/rnek-ios-example.gif" height="500" /> | <img src="/.github/images/rnek-android-example.gif" height="500" />
|
|
145
|
-
|
|
146
|
-
## Features
|
|
147
|
-
|
|
148
|
-
- Keyboard focus management and autofocus capabilities.
|
|
149
|
-
- Key press event handling.
|
|
150
|
-
- Focus management for `TextInput` and `Pressable` components.
|
|
151
|
-
- Customization of the `Halo Effect` and `tintColor` for iOS.
|
|
152
|
-
- Keyboard focus order.
|
|
153
|
-
|
|
154
61
|
## Installation
|
|
155
62
|
|
|
156
63
|
```sh
|
|
@@ -324,7 +231,7 @@ containerStyle | Style for the container | StyleProp<ViewStyle>
|
|
|
324
231
|
containerFocusStyle?: | Style applied to the container when focused | `FocusStyle`
|
|
325
232
|
tintType?: | Tint behavior type | `'default' \\| 'hover' \\| 'background' \\| 'none'`
|
|
326
233
|
FocusHoverComponent?: | Component displayed on focus | `\\| ReactElement \\| FunctionComponent \\| (() => ReactElement);`
|
|
327
|
-
onSubmitEditing?: | Extended `onSubmitEditing` for
|
|
234
|
+
onSubmitEditing?: | Extended `onSubmitEditing` for multiline input | `(e: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => void)`
|
|
328
235
|
...rest | Remaining TextInput props | `TextInputProps`
|
|
329
236
|
|
|
330
237
|
|
|
@@ -336,7 +243,7 @@ import { KeyboardExtendedBaseView } from 'react-native-external-keyboard';
|
|
|
336
243
|
<KeyboardExtendedBaseView
|
|
337
244
|
onKeyDownPress={...}
|
|
338
245
|
onKeyUpPress={...}
|
|
339
|
-
|
|
246
|
+
focusable
|
|
340
247
|
>
|
|
341
248
|
<Text>Content</Text>
|
|
342
249
|
</KeyboardExtendedBaseView>
|
|
@@ -2,6 +2,7 @@ package com.externalkeyboard;
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import com.externalkeyboard.modules.ExternalKeyboardModule;
|
|
5
|
+
import com.externalkeyboard.views.ExternalKeyboardLockView.ExternalKeyboardLockViewManager;
|
|
5
6
|
import com.externalkeyboard.views.ExternalKeyboardView.ExternalKeyboardViewManager;
|
|
6
7
|
import com.externalkeyboard.views.KeyboardFocusGroup.KeyboardFocusGroupManager;
|
|
7
8
|
import com.externalkeyboard.views.TextInputFocusWrapper.TextInputFocusWrapperManager;
|
|
@@ -50,6 +51,7 @@ public class ExternalKeyboardViewPackage extends TurboReactPackage {
|
|
|
50
51
|
viewManagers.add(new ExternalKeyboardViewManager());
|
|
51
52
|
viewManagers.add(new TextInputFocusWrapperManager());
|
|
52
53
|
viewManagers.add(new KeyboardFocusGroupManager());
|
|
54
|
+
viewManagers.add(new ExternalKeyboardLockViewManager());
|
|
53
55
|
|
|
54
56
|
return viewManagers;
|
|
55
57
|
}
|
|
@@ -7,6 +7,9 @@ import android.view.ViewGroup;
|
|
|
7
7
|
import java.util.HashMap;
|
|
8
8
|
import java.util.Map;
|
|
9
9
|
|
|
10
|
+
import androidx.annotation.Nullable;
|
|
11
|
+
import androidx.core.view.ViewCompat;
|
|
12
|
+
|
|
10
13
|
public class FocusHelper {
|
|
11
14
|
private static final int MASK_FOCUS_UP = 0b1;
|
|
12
15
|
private static final int MASK_FOCUS_DOWN = 0b10;
|
|
@@ -60,4 +63,71 @@ public class FocusHelper {
|
|
|
60
63
|
|
|
61
64
|
return null;
|
|
62
65
|
}
|
|
66
|
+
public static boolean isAccessible(@Nullable View view) {
|
|
67
|
+
return view != null && ViewCompat.isImportantForAccessibility(view);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public static View findFirstAccessible(@Nullable ViewGroup viewGroup, boolean ignoreRoot) {
|
|
71
|
+
if (viewGroup == null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!ignoreRoot && isAccessible(viewGroup)) {
|
|
76
|
+
return viewGroup;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (int i = 0; i < viewGroup.getChildCount(); i++) {
|
|
80
|
+
View child = viewGroup.getChildAt(i);
|
|
81
|
+
if (isAccessible(child)) {
|
|
82
|
+
return child;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (child instanceof ViewGroup) {
|
|
86
|
+
View accessibleChild = findFirstAccessible((ViewGroup) child, true);
|
|
87
|
+
if (accessibleChild != null) {
|
|
88
|
+
return accessibleChild;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public static View findFirstAccessible(@Nullable ViewGroup viewGroup) {
|
|
97
|
+
return findFirstAccessible(viewGroup, false);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public static View findFirstFocusable(@Nullable ViewGroup viewGroup, boolean ignoreRoot) {
|
|
101
|
+
if (viewGroup == null) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!ignoreRoot && isKeyboardFocusable(viewGroup)) {
|
|
106
|
+
return viewGroup;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (int i = 0; i < viewGroup.getChildCount(); i++) {
|
|
110
|
+
View child = viewGroup.getChildAt(i);
|
|
111
|
+
if (isKeyboardFocusable(child)) {
|
|
112
|
+
return child;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (child instanceof ViewGroup) {
|
|
116
|
+
View focusableChild = findFirstFocusable((ViewGroup) child, true);
|
|
117
|
+
if (focusableChild != null) {
|
|
118
|
+
return focusableChild;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public static View findFirstFocusable(@Nullable ViewGroup viewGroup) {
|
|
127
|
+
return findFirstFocusable(viewGroup, false);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private static boolean isKeyboardFocusable(View view) {
|
|
131
|
+
return view.isFocusable() && view.getVisibility() == View.VISIBLE && view.isEnabled();
|
|
132
|
+
}
|
|
63
133
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package com.externalkeyboard.views.ExternalKeyboardLockView;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.view.View;
|
|
5
|
+
import android.view.ViewGroup;
|
|
6
|
+
import android.view.accessibility.AccessibilityEvent;
|
|
7
|
+
|
|
8
|
+
import com.externalkeyboard.helper.FocusHelper;
|
|
9
|
+
import com.facebook.react.views.view.ReactViewGroup;
|
|
10
|
+
|
|
11
|
+
public class ExternalKeyboardLockView extends ReactViewGroup {
|
|
12
|
+
private int componentType;
|
|
13
|
+
private Boolean lockDisable = false;
|
|
14
|
+
|
|
15
|
+
public ExternalKeyboardLockView(Context context) {
|
|
16
|
+
super(context);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static boolean isParentOf(ViewGroup parent, View child) {
|
|
20
|
+
View current = child;
|
|
21
|
+
while (current.getParent() instanceof View) {
|
|
22
|
+
current = (View) current.getParent();
|
|
23
|
+
if (current == parent) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public void setComponentType(int value) {
|
|
31
|
+
this.componentType = value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public void setLockDisabled(boolean lockDisabled) {
|
|
35
|
+
this.lockDisable = lockDisabled;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Override
|
|
39
|
+
public View focusSearch(View focused, int direction) {
|
|
40
|
+
try {
|
|
41
|
+
LockService lockService = LockService.getInstance();
|
|
42
|
+
|
|
43
|
+
if (componentType == 1) {
|
|
44
|
+
View keyboardView = lockService.getKeyboardView();
|
|
45
|
+
if (keyboardView != null && keyboardView != focused) {
|
|
46
|
+
return keyboardView;
|
|
47
|
+
}
|
|
48
|
+
return super.focusSearch(focused, direction);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (componentType == 0) {
|
|
52
|
+
View nextView = super.focusSearch(focused, direction);
|
|
53
|
+
View result = isParentOf(this, nextView) ? nextView : focused;
|
|
54
|
+
lockService.setKeyboardView(result);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return super.focusSearch(focused, direction);
|
|
59
|
+
} catch (Exception e) {
|
|
60
|
+
return focused;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@Override
|
|
65
|
+
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
|
|
66
|
+
if (this.lockDisable) {
|
|
67
|
+
return super.onRequestSendAccessibilityEvent(child, event);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
|
|
71
|
+
return super.onRequestSendAccessibilityEvent(child, event);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
LockService lockService = LockService.getInstance();
|
|
75
|
+
|
|
76
|
+
if (componentType == 0) {
|
|
77
|
+
lockService.setView(child);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (componentType == 1) {
|
|
82
|
+
View storedView = lockService.getView();
|
|
83
|
+
if (storedView != null && storedView != child) {
|
|
84
|
+
storedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return super.onRequestSendAccessibilityEvent(child, event);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Override
|
|
93
|
+
protected void onAttachedToWindow() {
|
|
94
|
+
super.onAttachedToWindow();
|
|
95
|
+
this.post(this::focusFirstAccessible);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Override
|
|
99
|
+
protected void onDetachedFromWindow() {
|
|
100
|
+
super.onDetachedFromWindow();
|
|
101
|
+
LockService.getInstance().clear();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private void focusFirstAccessible() {
|
|
105
|
+
View firstAccessible = FocusHelper.findFirstAccessible(this);
|
|
106
|
+
if (firstAccessible != null) {
|
|
107
|
+
firstAccessible.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
View firstFocusable = FocusHelper.findFirstFocusable(this);
|
|
111
|
+
if (firstFocusable != null) {
|
|
112
|
+
firstFocusable.requestFocus();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
package com.externalkeyboard.views.ExternalKeyboardLockView;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.module.annotations.ReactModule;
|
|
4
|
+
import com.facebook.react.uimanager.ThemedReactContext;
|
|
5
|
+
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
6
|
+
|
|
7
|
+
@ReactModule(name = ExternalKeyboardLockViewManager.NAME)
|
|
8
|
+
public class ExternalKeyboardLockViewManager extends com.externalkeyboard.ExternalKeyboardLockViewManagerSpec<ExternalKeyboardLockView> {
|
|
9
|
+
public static final String NAME = "ExternalKeyboardLockView";
|
|
10
|
+
|
|
11
|
+
@Override
|
|
12
|
+
public String getName() {
|
|
13
|
+
return NAME;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Override
|
|
17
|
+
public ExternalKeyboardLockView createViewInstance(ThemedReactContext context) {
|
|
18
|
+
return new ExternalKeyboardLockView(context);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Override
|
|
22
|
+
@ReactProp(name = "componentType")
|
|
23
|
+
public void setComponentType(ExternalKeyboardLockView view, int value) {
|
|
24
|
+
view.setComponentType(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Override
|
|
28
|
+
@ReactProp(name = "lockDisabled")
|
|
29
|
+
public void setLockDisabled(ExternalKeyboardLockView view, boolean value) {
|
|
30
|
+
view.setLockDisabled(value);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardLockView/LockService.java
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
package com.externalkeyboard.views.ExternalKeyboardLockView;
|
|
2
|
+
|
|
3
|
+
import android.view.View;
|
|
4
|
+
|
|
5
|
+
import java.lang.ref.WeakReference;
|
|
6
|
+
|
|
7
|
+
public class LockService {
|
|
8
|
+
private static LockService instance;
|
|
9
|
+
|
|
10
|
+
private WeakReference<View> viewRef;
|
|
11
|
+
private WeakReference<View> keyboardViewRef;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
private LockService() {
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public static synchronized LockService getInstance() {
|
|
18
|
+
if (instance == null) {
|
|
19
|
+
instance = new LockService();
|
|
20
|
+
}
|
|
21
|
+
return instance;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public View getView() {
|
|
25
|
+
return viewRef != null ? viewRef.get() : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public void setView(View view) {
|
|
29
|
+
viewRef = new WeakReference<>(view);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public View getKeyboardView() {
|
|
33
|
+
return keyboardViewRef != null ? keyboardViewRef.get() : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public void setKeyboardView(View view) {
|
|
37
|
+
keyboardViewRef = new WeakReference<>(view);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public void clear() {
|
|
41
|
+
this.viewRef = null;
|
|
42
|
+
this.keyboardViewRef = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
package com.externalkeyboard;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.viewmanagers.ExternalKeyboardLockViewManagerInterface;
|
|
4
|
+
import com.facebook.react.views.view.ReactViewGroup;
|
|
5
|
+
import com.facebook.react.views.view.ReactViewManager;
|
|
6
|
+
|
|
7
|
+
public abstract class ExternalKeyboardLockViewManagerSpec<T extends ReactViewGroup> extends ReactViewManager implements ExternalKeyboardLockViewManagerInterface<T> {
|
|
8
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
package com.externalkeyboard;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import com.externalkeyboard.views.ExternalKeyboardLockView.ExternalKeyboardLockView;
|
|
5
|
+
import com.facebook.react.views.view.ReactViewManager;
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
public abstract class ExternalKeyboardLockViewManagerSpec<T extends ExternalKeyboardLockView> extends ReactViewManager {
|
|
9
|
+
public abstract void setComponentType(T view, int value);
|
|
10
|
+
public abstract void setLockDisabled(T view, boolean value);
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -13,46 +13,83 @@
|
|
|
13
13
|
#import "RNCEKVFocusEffectUtility.h"
|
|
14
14
|
|
|
15
15
|
@implementation RNCEKVFocusDelegate{
|
|
16
|
-
|
|
16
|
+
UIView<RNCEKVFocusProtocol>* _delegate;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
- (instancetype _Nonnull )initWithView:(UIView<RNCEKVFocusProtocol> *_Nonnull)delegate{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
self = [super init];
|
|
21
|
+
if (self) {
|
|
22
|
+
_delegate = delegate;
|
|
23
|
+
}
|
|
24
|
+
return self;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (UIView*)getFirstFocusable:(UIView*)view {
|
|
28
|
+
if(view.subviews.count == 0) return nil;
|
|
29
|
+
UIView* child = view.subviews[0];
|
|
30
|
+
if(child.canBecomeFocused) {
|
|
31
|
+
return child;
|
|
32
|
+
} else {
|
|
33
|
+
return [self getFirstFocusable: child];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
- (UIView *)focusingViewForGestureHandler {
|
|
39
|
+
UIView *view = _delegate.subviews.firstObject;
|
|
40
|
+
// Handle the special case for RNGestureHandlerButtonComponentView
|
|
41
|
+
if (view && [NSStringFromClass([view class]) isEqualToString:@"RNGestureHandlerButtonComponentView"] &&
|
|
42
|
+
view.subviews.count > 0) {
|
|
43
|
+
return view.subviews.firstObject;
|
|
44
|
+
}
|
|
45
|
+
return nil;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (UIView *)firstObject {
|
|
49
|
+
UIView *firstChild = _delegate.subviews.firstObject;
|
|
50
|
+
if (firstChild && firstChild.canBecomeFocused) {
|
|
51
|
+
return firstChild;
|
|
23
52
|
}
|
|
24
|
-
return
|
|
53
|
+
return nil;
|
|
25
54
|
}
|
|
26
55
|
|
|
56
|
+
|
|
57
|
+
|
|
27
58
|
// ToDo RNCEKV-2, Double check condition, count more then 1 means that a view can be a ViewGroup, bun when we use hover component it can be considered as ViewGroup instead of touchable component, it can be improved by flag, or by removing hover component from js implementation
|
|
28
|
-
- (UIView*)getFocusingView {
|
|
29
|
-
if(_delegate.isGroup) {
|
|
59
|
+
- (UIView *)getFocusingView {
|
|
60
|
+
if (_delegate.isGroup) {
|
|
30
61
|
return _delegate;
|
|
31
62
|
}
|
|
32
63
|
|
|
33
|
-
|
|
34
|
-
|
|
64
|
+
UIView *gestureHandlerView = [self focusingViewForGestureHandler];
|
|
65
|
+
if (gestureHandlerView) {
|
|
66
|
+
return gestureHandlerView;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
UIView *firstObject = [self firstObject];
|
|
70
|
+
if (firstObject) {
|
|
71
|
+
return firstObject;
|
|
35
72
|
}
|
|
36
73
|
|
|
37
74
|
return _delegate;
|
|
38
75
|
}
|
|
39
76
|
|
|
40
77
|
- (BOOL)canBecomeFocused {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
if(!_delegate.canBeFocused) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return [self getFocusingView] == _delegate;
|
|
45
82
|
}
|
|
46
83
|
|
|
47
84
|
- (NSNumber*)isFocusChanged:(UIFocusUpdateContext *)context {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
85
|
+
UIView* view = [self getFocusingView];
|
|
86
|
+
if(context.nextFocusedView == view) {
|
|
87
|
+
return @YES;
|
|
88
|
+
} else if (context.previouslyFocusedView == view) {
|
|
89
|
+
return @NO;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return nil;
|
|
56
93
|
}
|
|
57
94
|
|
|
58
95
|
|
package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusGuideDelegate/RNCEKVFocusGuideDelegate.h
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RNCEKVFocusGuideDelegate.h
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Artur Kalach on 16/07/2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#ifndef RNCEKVFocusGuideDelegate_h
|
|
9
|
+
#define RNCEKVFocusGuideDelegate_h
|
|
10
|
+
|
|
11
|
+
#import <Foundation/Foundation.h>
|
|
12
|
+
#import "RNCEKVFocusOrderProtocol.h"
|
|
13
|
+
#import "RNCEKVFocusGuideHelper.h"
|
|
14
|
+
|
|
15
|
+
@interface RNCEKVFocusGuideDelegate : NSObject
|
|
16
|
+
|
|
17
|
+
- (instancetype _Nonnull)initWithView:(UIView<RNCEKVFocusOrderProtocol> *_Nonnull)view;
|
|
18
|
+
|
|
19
|
+
//- (void)setLeftGuide:(UIView *_Nullable)view;
|
|
20
|
+
//- (void)setRightGuide:(UIView *_Nullable)view;
|
|
21
|
+
//- (void)setUpGuide:(UIView *_Nullable)view;
|
|
22
|
+
//- (void)setDownGuide:(UIView *_Nullable)view;
|
|
23
|
+
//
|
|
24
|
+
//- (void)removeLeftGuide;
|
|
25
|
+
//- (void)removeRightGuide;
|
|
26
|
+
//- (void)removeUpGuide;
|
|
27
|
+
//- (void)removeDownGuide;
|
|
28
|
+
|
|
29
|
+
- (void)setIsFocused:(BOOL)value;
|
|
30
|
+
|
|
31
|
+
- (void)setGuideFor:(RNCEKVFocusGuideDirection)direction withView: (UIView *_Nonnull)view;
|
|
32
|
+
- (void)removeGuideFor:(RNCEKVFocusGuideDirection)direction;
|
|
33
|
+
|
|
34
|
+
@end
|
|
35
|
+
|
|
36
|
+
#endif /* RNCEKVFocusGuideDelegate_h */
|