react-native-dodge-keyboard 1.0.6 → 1.0.8
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 +42 -6
- package/index.d.ts +1 -1
- package/index.js +22 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
react-native-dodge-keyboard is a tiny, zero-dependency library designed to flawlessly move your UI out of the way of the keyboard.
|
|
6
6
|
It is built to solve a long-standing React Native pain point: TextInputs inside ScrollViews, FlatLists, and custom scrollable layouts not being properly lifted when focused.
|
|
7
7
|
|
|
8
|
-
Unlike KeyboardAvoidingView and older third-party libraries, react-native-dodge-keyboard:
|
|
8
|
+
Unlike `KeyboardAvoidingView` and older third-party libraries, react-native-dodge-keyboard:
|
|
9
9
|
|
|
10
|
-
- Works with any scrollable container
|
|
10
|
+
- Works with any scrollable container (ScrollView, FlatList, SectionList, ...more)
|
|
11
11
|
- Supports dynamic layout sizes
|
|
12
|
+
- Handles elements that are not inside a scrollable view.
|
|
12
13
|
- Handles scrollviews placed anywhere (nested, offset, inside modals, bottom sheets, etc.)
|
|
13
14
|
- Works on both iOS and Android
|
|
14
15
|
- Requires zero configuration
|
|
@@ -27,6 +28,13 @@ or using yarn
|
|
|
27
28
|
yarn add react-native-dodge-keyboard
|
|
28
29
|
```
|
|
29
30
|
|
|
31
|
+
## Demo
|
|
32
|
+
|
|
33
|
+
<p>
|
|
34
|
+
<img src="https://github.com/deflexable/react-native-dodge-keyboard/blob/main/screenshots/main.gif" width="260">
|
|
35
|
+
<img src="https://github.com/deflexable/react-native-dodge-keyboard/blob/main/screenshots/android_main.gif" width="260">
|
|
36
|
+
</p>
|
|
37
|
+
|
|
30
38
|
## Usage
|
|
31
39
|
|
|
32
40
|
Wrap your screen (or only the part you want to dodge) with the DodgeKeyboard component.
|
|
@@ -34,18 +42,46 @@ Wrap your screen (or only the part you want to dodge) with the DodgeKeyboard com
|
|
|
34
42
|
### Basic Example
|
|
35
43
|
|
|
36
44
|
```js
|
|
37
|
-
import DodgeKeyboard from 'react-native-dodge-keyboard';
|
|
45
|
+
import { DodgeKeyboard } from 'react-native-dodge-keyboard';
|
|
38
46
|
import { ScrollView, TextInput } from 'react-native';
|
|
39
47
|
|
|
40
|
-
export default function
|
|
48
|
+
export default function TestScreen() {
|
|
41
49
|
|
|
42
50
|
return (
|
|
43
51
|
<DodgeKeyboard>
|
|
44
|
-
<ScrollView
|
|
52
|
+
<ScrollView style={{ flex: 1 }}>
|
|
45
53
|
<TextInput placeholder="Name" style={styles.input} />
|
|
46
54
|
<TextInput multiline placeholder="Message" style={styles.multiline} />
|
|
47
55
|
</ScrollView>
|
|
48
56
|
</DodgeKeyboard>
|
|
49
57
|
);
|
|
50
58
|
}
|
|
51
|
-
```
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Known Issues
|
|
62
|
+
|
|
63
|
+
### Android Soft Input
|
|
64
|
+
Some Android devices handle keyboard dodging natively, which can lead to unexpected behavior when used with this library. To disable the native behavior and avoid issues, set `android:windowSoftInputMode="adjustNothing"` in your AndroidManifest.xml.
|
|
65
|
+
|
|
66
|
+
## Advance Use Cases
|
|
67
|
+
|
|
68
|
+
### Dodging custom views
|
|
69
|
+
Out of the box, `TextInput` is the only component that supports keyboard dodging. To enable this behavior for other components, pass `dodge_keyboard_input={true}` as a prop.
|
|
70
|
+
|
|
71
|
+
You can also listen to `checkIfElementIsFocused` prop and return whether the element passed as an argument is currently focused and needs to dodge the keyboard.
|
|
72
|
+
|
|
73
|
+
Kindly check [examples/CustomFocusDodge.jsx](https://github.com/deflexable/react-native-dodge-keyboard/blob/main/examples/CustomFocusDodge.jsx) for example on this approach.
|
|
74
|
+
|
|
75
|
+
A demo of the example is attach below:
|
|
76
|
+
|
|
77
|
+
<img src="https://github.com/deflexable/react-native-dodge-keyboard/blob/main/screenshots/custom.gif" width="260">
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Dodging static element
|
|
81
|
+
To dodge `TextInput` or component with prop `dodge_keyboard_input={true}` that are not inside a scrollable view, you need to listen to `onHandleDodging` to manually lift up the component yourself.
|
|
82
|
+
|
|
83
|
+
Kindly check [examples/ManualLifting.jsx](https://github.com/deflexable/react-native-dodge-keyboard/blob/main/examples/ManualLifting.jsx) for example on this approach.
|
|
84
|
+
|
|
85
|
+
A demo of the example is attach below:
|
|
86
|
+
|
|
87
|
+
<img src="https://github.com/deflexable/react-native-dodge-keyboard/blob/main/screenshots/static.gif" width="260">
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Children, cloneElement, createElement, forwardRef, isValidElement, memo, useEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { Children, cloneElement, createElement, forwardRef, isValidElement, memo, useEffect, useMemo, useRef, useState, useImperativeHandle } from "react";
|
|
2
2
|
import { Animated, Dimensions, findNodeHandle, Keyboard, Platform, StyleSheet, UIManager, useAnimatedValue } from "react-native";
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export const DodgeKeyboard = forwardRef(({ children, offset = 10, disabled, onHandleDodging, disableTagCheck, checkIfElementIsFocused }, ref) => {
|
|
5
5
|
if (checkIfElementIsFocused !== undefined) {
|
|
6
6
|
if (typeof checkIfElementIsFocused !== 'function')
|
|
7
7
|
throw 'checkIfElementIsFocused should be a function';
|
|
@@ -97,7 +97,7 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
97
97
|
if (!eventContext?.fromTimer && resizerTimer.current === undefined)
|
|
98
98
|
resizerTimer.current = setTimeout(() => {
|
|
99
99
|
doDodgeKeyboard.current(undefined, undefined, { fromTimer: true });
|
|
100
|
-
},
|
|
100
|
+
}, 500);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
const checkFocused = checkIfElementIsFocused || (r => r?.isFocused?.());
|
|
@@ -140,14 +140,14 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
140
140
|
});
|
|
141
141
|
}),
|
|
142
142
|
new Promise(resolve => {
|
|
143
|
-
|
|
143
|
+
UIManager.measure(findNodeHandle(inputObj), (x, y, width, height, pageX, pageY) => { // y is dynamic
|
|
144
144
|
resolve({ py: pageY, layout: { x, y, width, height, pageX, pageY } });
|
|
145
145
|
});
|
|
146
146
|
}),
|
|
147
147
|
new Promise((resolve, reject) => {
|
|
148
|
-
|
|
148
|
+
UIManager.measureLayout(findNodeHandle(inputObj), findNodeHandle(scrollRef), reject, (l, t, width, height) => { // t is fixed
|
|
149
149
|
resolve({ t, h: height, relativeLayout: { left: l, top: t, width, height } });
|
|
150
|
-
}
|
|
150
|
+
});
|
|
151
151
|
})
|
|
152
152
|
]).then(([{ h: sh, py: sy, scrollLayout }, { py: y, layout }, { t, h, relativeLayout }]) => {
|
|
153
153
|
|
|
@@ -198,7 +198,7 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
} else {
|
|
201
|
+
} else if (!visible) {
|
|
202
202
|
setCurrentPaddedScroller();
|
|
203
203
|
clearPreviousDodge();
|
|
204
204
|
}
|
|
@@ -224,7 +224,14 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
224
224
|
useEffect(() => {
|
|
225
225
|
if (currentPaddedScroller) {
|
|
226
226
|
const ref = viewRefsMap.current[paddedId]?.scrollRef;
|
|
227
|
-
|
|
227
|
+
if (Platform.OS === 'android') {
|
|
228
|
+
tryPerformScroll(ref, paddedScroll, false);
|
|
229
|
+
} else {
|
|
230
|
+
// this seem to be removing `the flash bang` on IOS
|
|
231
|
+
setTimeout(() => {
|
|
232
|
+
tryPerformScroll(ref, paddedScroll, false);
|
|
233
|
+
}, 1);
|
|
234
|
+
}
|
|
228
235
|
}
|
|
229
236
|
}, [currentPaddedScroller]);
|
|
230
237
|
|
|
@@ -232,6 +239,10 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
232
239
|
doDodgeKeyboard.current();
|
|
233
240
|
}, [offset, !disabled]);
|
|
234
241
|
|
|
242
|
+
useImperativeHandle(ref, () => ({
|
|
243
|
+
trigger: () => doDodgeKeyboard.current()
|
|
244
|
+
}), []);
|
|
245
|
+
|
|
235
246
|
useEffect(() => {
|
|
236
247
|
if (disabled) return;
|
|
237
248
|
const frameListener = Keyboard.addListener('keyboardDidChangeFrame', e => doDodgeKeyboard.current(e));
|
|
@@ -322,7 +333,7 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
322
333
|
...inputNode.props,
|
|
323
334
|
__dodging_keyboard: true,
|
|
324
335
|
onFocus: (...args) => {
|
|
325
|
-
doDodgeKeyboard.current();
|
|
336
|
+
doDodgeKeyboard.current(lastKeyboardEvent.current);
|
|
326
337
|
return inputNode.props?.onFocus?.(...args);
|
|
327
338
|
},
|
|
328
339
|
onLayout: (...args) => {
|
|
@@ -381,7 +392,7 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
381
392
|
},
|
|
382
393
|
...isStandalone ? {
|
|
383
394
|
onFocus: (...args) => {
|
|
384
|
-
doDodgeKeyboard.current();
|
|
395
|
+
doDodgeKeyboard.current(lastKeyboardEvent.current);
|
|
385
396
|
return node.props?.onFocus?.(...args);
|
|
386
397
|
}
|
|
387
398
|
} : {},
|
|
@@ -424,7 +435,7 @@ export default function ({ children, offset = 10, disabled, onHandleDodging, dis
|
|
|
424
435
|
{children}
|
|
425
436
|
</ReactHijacker>
|
|
426
437
|
);
|
|
427
|
-
};
|
|
438
|
+
});
|
|
428
439
|
|
|
429
440
|
const niceFunction = (func, message) => {
|
|
430
441
|
return (...args) => {
|
package/package.json
CHANGED