Commit 347fef3
feat:
## 📜 Description
Added `KeyboardExtender` component.
## 💡 Motivation and Context
This is a part of a big research that I did over several months.
This PR implements `KeyboardExtender` component. On iOS it utilizes
native capabilities of the OS and on Android it uses polyfill
(`KeyboardBackgroundView`+`KeyboardStickyView`+`useKeyboardAnimation`
for `opacity` animation).
That component aims to help developers to build various keyboard
extensions which will be shown above the keyboard and visually mimic to
it. The initial idea of implementation was inspired by Revolut app:
<div align="center">
<video
src="https://github.com/user-attachments/assets/320da57f-7ad8-41e3-931a-1be7780fc6f8">
</div>
And the `KeyboardExtender` ideally fits for building such things,
another example is:
<div align="center">
<img
src="https://github.com/user-attachments/assets/bb338e9b-b2f6-4a8a-a933-babe94a662fd"
width="300">
</div>
And I think developers can take inspiration and build even more advanced
features (such as voice search etc.)
To implement this on iOS I used
[UIInputView](https://developer.apple.com/documentation/uikit/uiinputview#overview)
with
[keyboard](https://developer.apple.com/documentation/uikit/uiinputview/style/keyboard)
style.
> [!WARNING]
> On iOS 26 it looks bad and the view is not getting embedded into the
keyboard. See [tweet](https://x.com/ziusko/status/1932492809258381350)
about it. For now let's wait for Apple fixes and if they are not going
to fix it, then most likely we'll have to write separate implementation
for iSO 26 to use liquid glass design.
On Android I'm trying to re--create iOS effect, but it's obviously not
possible to match it fully, so I'm trying to make it looking as close as
possible to. For sure I can not fully hide the view and make it moving
together with keyboard from the very beginning, so I decided to use
`opacity` interpolation and always show it above the keyboard (using
`KeyboardStickyView`).
> I decided not to use parallax animation (as in `KeyboardToolbar`)
here. Let's see if `opacity` + frame-in-frame motion looks better than
parallax effect 👀
<hr>
Last but not least - for now I decided to make this component very
simple and barely customizable (I expose only `enabled` property). And
it can cause certain issues, for example if you switch between QWERTY
keyboard (where you don't want `KeyboardExtender` to be present) to
numeric (where you'd like to see `KeyboardExtender`), then you will see
how react updates everything asynchronously and for a fraction of a
second you will see QWERTY-keyboard with `KeyboardExtender`.
Theoretically I could introduce binding through `nativeID` and "connect"
specific inputs with specific extenders, but for now let's not do an
over-engineering and try to handle all cases. Let's handle everything
step-by-step and when we get a bug report then we can do another round
of investigation how such thing can be implemented 👀
## 📢 Changelog
<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->
### Docs
- added new page for `KeyboardExtender`;
- added new chapter in `Components Overview`;
- mention `KeyboardExtender` feature in `README`;
### E2E
- added new test for `KeyboardExtender`;
### JS
- added `KeyboardExtender` component;
- added `KeyboardExtender` spec;
- added `KeyboardExtender` mock.
### iOS
- expose `KeyboardExtender` view;
### C++
- added c++ bindings (shadow nodes, component descriptor, etc.) for
`KeyboardExtender`;
## 🤔 How Has This Been Tested?
Tested manually on:
- iPhone 15 Pro (iOS 17.5, simulator)
- iPhone 16 Pro (iOS 18.4, simulator)
- iPhone 6s (iOS 15.5, real device)
- iPhone 16 Pro (iOS 26.0, simulator) - doesn't look good there, filled
bug to Apple
- Medium Phone API 35 (emulator)
- Pixel 7 Pro (API 36, real device)
- Xiaomi Redmi Note 5 Pro (API 28, real device)
## 📸 Screenshots (if appropriate):
|iOS|Android|
|----|--------|
|<video
src="https://github.com/user-attachments/assets/a5c7c515-6b0e-4966-a74c-78e00619c137">|<video
src="https://github.com/user-attachments/assets/f6119e63-ae41-4e0f-b732-e112f2e7c4e6">|
## 📝 Checklist
- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>KeyboardExtender (#982)1 parent a57fa4b commit 347fef3
52 files changed
Lines changed: 1055 additions & 8 deletions
File tree
- FabricExample
- __tests__
- __snapshots__
- src
- constants
- screens/Examples
- KeyboardExtender
- Main
- android/src/main/java/com/reactnativekeyboardcontroller/views/background
- common/cpp/react/renderer/components/reactnativekeyboardcontroller
- docs/docs
- api
- keyboard-background-view
- keyboard-extender
- guides
- e2e
- kit
- assets
- android
- e2e_emulator_28
- e2e_emulator_31
- ios
- iPhone 13 Pro
- iPhone 14 Pro
- iPhone 15 Pro
- iPhone 16 Pro
- example
- __tests__
- __snapshots__
- src
- constants
- screens/Examples
- KeyboardExtender
- Main
- ios/views
- jest
- src
- specs
- types
- views
- KeyboardExtender
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 16 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
57 | 73 | | |
58 | 74 | | |
59 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
82 | 87 | | |
83 | 88 | | |
84 | 89 | | |
| |||
111 | 116 | | |
112 | 117 | | |
113 | 118 | | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
114 | 123 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
29 | 30 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| |||
52 | 53 | | |
53 | 54 | | |
54 | 55 | | |
| 56 | + | |
55 | 57 | | |
56 | 58 | | |
57 | 59 | | |
| |||
132 | 134 | | |
133 | 135 | | |
134 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
135 | 141 | | |
136 | 142 | | |
137 | 143 | | |
| |||
256 | 262 | | |
257 | 263 | | |
258 | 264 | | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
259 | 270 | | |
260 | 271 | | |
261 | 272 | | |
| |||
Lines changed: 115 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
67 | | - | |
| 67 | + | |
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
150 | 156 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| |||
Lines changed: 3 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
7 | 6 | | |
8 | 7 | | |
9 | 8 | | |
10 | 9 | | |
11 | 10 | | |
| 11 | + | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
79 | | - | |
| 80 | + | |
80 | 81 | | |
81 | 82 | | |
82 | 83 | | |
| |||
0 commit comments