react-native-keyboard-toolkit 1.0.0 → 1.0.1
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/CHANGELOG.md +9 -0
- package/README.md +135 -893
- package/dist/components/KeyboardAwareView.d.ts +1 -1
- package/dist/components/KeyboardAwareView.js +59 -10
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/scrollManager.d.ts +2 -0
- package/dist/utils/scrollManager.js +5 -0
- package/docs/android.md +5 -0
- package/docs/api.md +5 -2
- package/docs/ios.md +5 -0
- package/docs/troubleshooting.md +8 -0
- package/package.json +5 -5
- package/src/components/KeyboardAwareView.tsx +78 -18
- package/src/types/index.ts +1 -1
- package/src/utils/scrollManager.ts +12 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { KeyboardAwareViewProps } from '../types';
|
|
3
3
|
import type { KeyboardAwareViewRef } from '../types';
|
|
4
|
-
/**
|
|
4
|
+
/** Adds keyboard spacing to in-flow content and keeps absolute footers visible. */
|
|
5
5
|
export declare const KeyboardAwareView: React.ForwardRefExoticComponent<KeyboardAwareViewProps & React.RefAttributes<KeyboardAwareViewRef>>;
|
|
6
6
|
export default KeyboardAwareView;
|
|
@@ -7,14 +7,20 @@ const react_native_1 = require("react-native");
|
|
|
7
7
|
const constants_1 = require("../utils/constants");
|
|
8
8
|
const useKeyboardState_1 = require("../hooks/useKeyboardState");
|
|
9
9
|
const scrollManager_1 = require("../utils/scrollManager");
|
|
10
|
-
/**
|
|
10
|
+
/** Adds keyboard spacing to in-flow content and keeps absolute footers visible. */
|
|
11
11
|
exports.KeyboardAwareView = (0, react_1.forwardRef)(function KeyboardAwareViewComponent({ behavior = 'smart', keyboardSpacing = constants_1.DEFAULT_KEYBOARD_SPACING, enabled = true, style, onLayout, ...props }, forwardedRef) {
|
|
12
12
|
const keyboard = (0, useKeyboardState_1.useKeyboardState)();
|
|
13
13
|
const viewRef = (0, react_1.useRef)(null);
|
|
14
14
|
(0, react_1.useImperativeHandle)(forwardedRef, () => viewRef.current, []);
|
|
15
15
|
const measurementSequence = (0, react_1.useRef)(0);
|
|
16
16
|
const [keyboardOverlap, setKeyboardOverlap] = (0, react_1.useState)(0);
|
|
17
|
+
const [keyboardDisplacement, setKeyboardDisplacement] = (0, react_1.useState)(0);
|
|
18
|
+
const animatedDisplacement = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
17
19
|
const flattenedStyle = react_native_1.StyleSheet.flatten(style);
|
|
20
|
+
const isAbsolutelyPositioned = flattenedStyle?.position === 'absolute';
|
|
21
|
+
const spacing = Number.isFinite(keyboardSpacing)
|
|
22
|
+
? Math.max(0, keyboardSpacing)
|
|
23
|
+
: 0;
|
|
18
24
|
const basePadding = typeof flattenedStyle?.paddingBottom === 'number'
|
|
19
25
|
? flattenedStyle.paddingBottom
|
|
20
26
|
: 0;
|
|
@@ -28,36 +34,79 @@ exports.KeyboardAwareView = (0, react_1.forwardRef)(function KeyboardAwareViewCo
|
|
|
28
34
|
if (sequence !== measurementSequence.current || !keyboard.isVisible) {
|
|
29
35
|
return;
|
|
30
36
|
}
|
|
31
|
-
|
|
37
|
+
if (!isAbsolutelyPositioned) {
|
|
38
|
+
setKeyboardOverlap((0, scrollManager_1.calculateKeyboardOverlap)({ y, height }, keyboard.height, keyboard.screenY));
|
|
39
|
+
}
|
|
40
|
+
setKeyboardDisplacement(isAbsolutelyPositioned && typeof keyboard.screenY === 'number'
|
|
41
|
+
? (0, scrollManager_1.calculateKeyboardDisplacement)({ y, height }, keyboard.screenY, spacing)
|
|
42
|
+
: 0);
|
|
32
43
|
});
|
|
33
|
-
}, [
|
|
44
|
+
}, [
|
|
45
|
+
isAbsolutelyPositioned,
|
|
46
|
+
keyboard.height,
|
|
47
|
+
keyboard.isVisible,
|
|
48
|
+
keyboard.screenY,
|
|
49
|
+
spacing,
|
|
50
|
+
]);
|
|
34
51
|
(0, react_1.useEffect)(() => {
|
|
35
52
|
if (!keyboard.isVisible) {
|
|
36
53
|
measurementSequence.current += 1;
|
|
37
54
|
setKeyboardOverlap(0);
|
|
55
|
+
setKeyboardDisplacement(0);
|
|
38
56
|
return;
|
|
39
57
|
}
|
|
40
58
|
const frame = requestAnimationFrame(measureOverlap);
|
|
41
59
|
return () => cancelAnimationFrame(frame);
|
|
42
60
|
}, [keyboard.isVisible, keyboard.height, keyboard.screenY, measureOverlap]);
|
|
61
|
+
const shouldAdjust = enabled &&
|
|
62
|
+
keyboard.isVisible &&
|
|
63
|
+
behavior !== 'none' &&
|
|
64
|
+
behavior !== 'resize';
|
|
65
|
+
(0, react_1.useEffect)(() => {
|
|
66
|
+
if (!isAbsolutelyPositioned) {
|
|
67
|
+
animatedDisplacement.setValue(0);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const animation = react_native_1.Animated.timing(animatedDisplacement, {
|
|
71
|
+
toValue: shouldAdjust ? -keyboardDisplacement : 0,
|
|
72
|
+
duration: keyboard.animationDuration,
|
|
73
|
+
useNativeDriver: true,
|
|
74
|
+
});
|
|
75
|
+
animation.start();
|
|
76
|
+
return () => animation.stop();
|
|
77
|
+
}, [
|
|
78
|
+
animatedDisplacement,
|
|
79
|
+
isAbsolutelyPositioned,
|
|
80
|
+
keyboard.animationDuration,
|
|
81
|
+
keyboardDisplacement,
|
|
82
|
+
shouldAdjust,
|
|
83
|
+
]);
|
|
43
84
|
const handleLayout = (0, react_1.useCallback)((event) => {
|
|
44
85
|
onLayout?.(event);
|
|
45
86
|
measureOverlap();
|
|
46
87
|
}, [measureOverlap, onLayout]);
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
behavior !== 'none' &&
|
|
50
|
-
behavior !== 'resize' &&
|
|
88
|
+
const adjustPadding = shouldAdjust &&
|
|
89
|
+
!isAbsolutelyPositioned &&
|
|
51
90
|
keyboardOverlap > 0 &&
|
|
52
91
|
typeof flattenedStyle?.paddingBottom !== 'string';
|
|
53
|
-
|
|
92
|
+
const stickyTransform = isAbsolutelyPositioned
|
|
93
|
+
? [
|
|
94
|
+
...(Array.isArray(flattenedStyle?.transform)
|
|
95
|
+
? flattenedStyle.transform
|
|
96
|
+
: []),
|
|
97
|
+
{ translateY: animatedDisplacement },
|
|
98
|
+
]
|
|
99
|
+
: undefined;
|
|
100
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { ...props, ref: viewRef, onLayout: handleLayout, style: adjustPadding
|
|
54
101
|
? [
|
|
55
102
|
style,
|
|
56
103
|
{
|
|
57
|
-
paddingBottom: Math.max(basePadding, keyboardOverlap +
|
|
104
|
+
paddingBottom: Math.max(basePadding, keyboardOverlap + spacing),
|
|
58
105
|
},
|
|
59
106
|
]
|
|
60
|
-
:
|
|
107
|
+
: isAbsolutelyPositioned
|
|
108
|
+
? [style, { transform: stickyTransform }]
|
|
109
|
+
: style }));
|
|
61
110
|
});
|
|
62
111
|
exports.KeyboardAwareView.displayName = 'KeyboardAwareView';
|
|
63
112
|
exports.default = exports.KeyboardAwareView;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface KeyboardAwareScrollViewProps extends ScrollViewProps {
|
|
|
23
23
|
enabled?: boolean;
|
|
24
24
|
}
|
|
25
25
|
export interface KeyboardAwareViewProps extends ViewProps {
|
|
26
|
-
/** Defaults to `smart
|
|
26
|
+
/** Defaults to `smart`; absolute-positioned views are moved above the keyboard. */
|
|
27
27
|
behavior?: KeyboardBehavior;
|
|
28
28
|
/** Extra space between the view content and the keyboard, in points. */
|
|
29
29
|
keyboardSpacing?: number;
|
|
@@ -10,6 +10,8 @@ export interface ScrollCalculation {
|
|
|
10
10
|
offset: number;
|
|
11
11
|
delta: number;
|
|
12
12
|
}
|
|
13
|
+
/** Return how far a bottom edge must move to clear the keyboard and spacing. */
|
|
14
|
+
export declare function calculateKeyboardDisplacement(viewport: ScrollViewport, keyboardTop: number, spacing: number): number;
|
|
13
15
|
/** Return the part of the measured view actually covered by the keyboard. */
|
|
14
16
|
export declare function calculateKeyboardOverlap(viewport: ScrollViewport, keyboardHeight: number, keyboardTop?: number): number;
|
|
15
17
|
/**
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateKeyboardDisplacement = calculateKeyboardDisplacement;
|
|
3
4
|
exports.calculateKeyboardOverlap = calculateKeyboardOverlap;
|
|
4
5
|
exports.calculateScrollOffset = calculateScrollOffset;
|
|
6
|
+
/** Return how far a bottom edge must move to clear the keyboard and spacing. */
|
|
7
|
+
function calculateKeyboardDisplacement(viewport, keyboardTop, spacing) {
|
|
8
|
+
return Math.max(0, viewport.y + viewport.height - keyboardTop + Math.max(0, spacing));
|
|
9
|
+
}
|
|
5
10
|
/** Return the part of the measured view actually covered by the keyboard. */
|
|
6
11
|
function calculateKeyboardOverlap(viewport, keyboardHeight, keyboardTop) {
|
|
7
12
|
const viewportBottom = viewport.y + viewport.height;
|
package/docs/android.md
CHANGED
|
@@ -12,6 +12,11 @@ ended the viewport at that frame, the measured overlap is zero and no second
|
|
|
12
12
|
keyboard-height inset is added. OEM keyboards and edge-to-edge configurations
|
|
13
13
|
can still report coordinates relative to different window origins.
|
|
14
14
|
|
|
15
|
+
For a sticky submit action, put an absolutely positioned
|
|
16
|
+
`KeyboardAwareView` outside the scroll view. With `adjustResize`, Android may
|
|
17
|
+
already move the footer as the window changes size; the measured keyboard frame
|
|
18
|
+
is used to apply only any remaining displacement and configured spacing.
|
|
19
|
+
|
|
15
20
|
## Device checklist
|
|
16
21
|
|
|
17
22
|
- Test Android 10+ devices/emulators and the app's minimum supported OS.
|
package/docs/api.md
CHANGED
|
@@ -22,8 +22,11 @@ underlying React Native `ScrollView`.
|
|
|
22
22
|
|
|
23
23
|
Extends `ViewProps`. Supports `behavior`, `keyboardSpacing`, and `enabled`.
|
|
24
24
|
Smart, scroll, and padding add bottom padding for the measured overlap. Resize
|
|
25
|
-
and none do not
|
|
26
|
-
|
|
25
|
+
and none do not adjust the layout. When `style.position` is `absolute`, the
|
|
26
|
+
component animates the view upward by the distance from its measured bottom
|
|
27
|
+
edge to the keyboard frame, plus `keyboardSpacing`. It returns to its original
|
|
28
|
+
position when the keyboard hides. This view does not scroll to reveal a hidden
|
|
29
|
+
child. Its ref points to the underlying React Native `View`.
|
|
27
30
|
|
|
28
31
|
## `KeyboardProvider`
|
|
29
32
|
|
package/docs/ios.md
CHANGED
|
@@ -7,6 +7,11 @@ temporary scroll range, so safe-area-constrained viewports do not blindly add
|
|
|
7
7
|
the full keyboard height. The library does not set safe-area, navigation, or
|
|
8
8
|
modal presentation configuration.
|
|
9
9
|
|
|
10
|
+
An absolutely positioned `KeyboardAwareView` uses the same keyboard frame and
|
|
11
|
+
event duration to animate a sticky footer above the keyboard. Place it outside
|
|
12
|
+
the scroll view and account for its height in the scroll view's content padding
|
|
13
|
+
and `keyboardSpacing`.
|
|
14
|
+
|
|
10
15
|
## Device checklist
|
|
11
16
|
|
|
12
17
|
- Test iPhone and iPad layouts with safe areas and the home indicator.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
- If a custom input wrapper delays focus, call `refresh()` from
|
|
10
10
|
`useFocusedInput` after its layout settles.
|
|
11
11
|
|
|
12
|
+
## A sticky submit button is behind the keyboard
|
|
13
|
+
|
|
14
|
+
Wrap the footer in `KeyboardAwareView` and set `position: 'absolute'` on that
|
|
15
|
+
component's style. The view then measures its bottom edge and moves above the
|
|
16
|
+
keyboard by the covered distance plus `keyboardSpacing`. Keep it outside the
|
|
17
|
+
scroll view and give the scroll content enough bottom padding to scroll above
|
|
18
|
+
the footer.
|
|
19
|
+
|
|
12
20
|
## Android content is moved twice
|
|
13
21
|
|
|
14
22
|
Use `adjustResize` as the standard window mode and avoid adding a second
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-keyboard-toolkit",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Keyboard-aware
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Keyboard-aware scrolling, views, sticky form actions, and keyboard hooks for React Native on iOS and Android.",
|
|
5
5
|
"author": "Denish Dabhi <dabhidenish57@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/DenishDabhi/react-native-keyboard-toolkit#readme",
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
"android",
|
|
45
45
|
"typescript",
|
|
46
46
|
"keyboard-dismiss",
|
|
47
|
-
"keyboard-handling"
|
|
47
|
+
"keyboard-handling",
|
|
48
|
+
"sticky-button",
|
|
49
|
+
"sticky-footer"
|
|
48
50
|
],
|
|
49
51
|
"publishConfig": {
|
|
50
52
|
"access": "public"
|
|
@@ -67,8 +69,6 @@
|
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
71
|
"@babel/core": "^7.25.2",
|
|
70
|
-
"@babel/preset-env": "^7.25.3",
|
|
71
|
-
"@babel/runtime": "^7.25.0",
|
|
72
72
|
"@react-native-community/cli": "20.2.0",
|
|
73
73
|
"@react-native-community/cli-platform-android": "20.2.0",
|
|
74
74
|
"@react-native-community/cli-platform-ios": "20.2.0",
|
|
@@ -6,14 +6,17 @@ import React, {
|
|
|
6
6
|
useRef,
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
|
-
import {
|
|
9
|
+
import { Animated, StyleSheet } from 'react-native';
|
|
10
10
|
import type { KeyboardAwareViewProps } from '../types';
|
|
11
11
|
import type { KeyboardAwareViewRef } from '../types';
|
|
12
12
|
import { DEFAULT_KEYBOARD_SPACING } from '../utils/constants';
|
|
13
13
|
import { useKeyboardState } from '../hooks/useKeyboardState';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
calculateKeyboardDisplacement,
|
|
16
|
+
calculateKeyboardOverlap,
|
|
17
|
+
} from '../utils/scrollManager';
|
|
15
18
|
|
|
16
|
-
/**
|
|
19
|
+
/** Adds keyboard spacing to in-flow content and keeps absolute footers visible. */
|
|
17
20
|
export const KeyboardAwareView: React.ForwardRefExoticComponent<
|
|
18
21
|
KeyboardAwareViewProps & React.RefAttributes<KeyboardAwareViewRef>
|
|
19
22
|
> = forwardRef<KeyboardAwareViewRef, KeyboardAwareViewProps>(
|
|
@@ -33,7 +36,13 @@ export const KeyboardAwareView: React.ForwardRefExoticComponent<
|
|
|
33
36
|
useImperativeHandle(forwardedRef, () => viewRef.current!, []);
|
|
34
37
|
const measurementSequence = useRef(0);
|
|
35
38
|
const [keyboardOverlap, setKeyboardOverlap] = useState(0);
|
|
39
|
+
const [keyboardDisplacement, setKeyboardDisplacement] = useState(0);
|
|
40
|
+
const animatedDisplacement = useRef(new Animated.Value(0)).current;
|
|
36
41
|
const flattenedStyle = StyleSheet.flatten(style);
|
|
42
|
+
const isAbsolutelyPositioned = flattenedStyle?.position === 'absolute';
|
|
43
|
+
const spacing = Number.isFinite(keyboardSpacing)
|
|
44
|
+
? Math.max(0, keyboardSpacing)
|
|
45
|
+
: 0;
|
|
37
46
|
const basePadding =
|
|
38
47
|
typeof flattenedStyle?.paddingBottom === 'number'
|
|
39
48
|
? flattenedStyle.paddingBottom
|
|
@@ -49,26 +58,69 @@ export const KeyboardAwareView: React.ForwardRefExoticComponent<
|
|
|
49
58
|
if (sequence !== measurementSequence.current || !keyboard.isVisible) {
|
|
50
59
|
return;
|
|
51
60
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
if (!isAbsolutelyPositioned) {
|
|
62
|
+
setKeyboardOverlap(
|
|
63
|
+
calculateKeyboardOverlap(
|
|
64
|
+
{ y, height },
|
|
65
|
+
keyboard.height,
|
|
66
|
+
keyboard.screenY,
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
setKeyboardDisplacement(
|
|
71
|
+
isAbsolutelyPositioned && typeof keyboard.screenY === 'number'
|
|
72
|
+
? calculateKeyboardDisplacement(
|
|
73
|
+
{ y, height },
|
|
74
|
+
keyboard.screenY,
|
|
75
|
+
spacing,
|
|
76
|
+
)
|
|
77
|
+
: 0,
|
|
58
78
|
);
|
|
59
79
|
});
|
|
60
|
-
}, [
|
|
80
|
+
}, [
|
|
81
|
+
isAbsolutelyPositioned,
|
|
82
|
+
keyboard.height,
|
|
83
|
+
keyboard.isVisible,
|
|
84
|
+
keyboard.screenY,
|
|
85
|
+
spacing,
|
|
86
|
+
]);
|
|
61
87
|
|
|
62
88
|
useEffect(() => {
|
|
63
89
|
if (!keyboard.isVisible) {
|
|
64
90
|
measurementSequence.current += 1;
|
|
65
91
|
setKeyboardOverlap(0);
|
|
92
|
+
setKeyboardDisplacement(0);
|
|
66
93
|
return;
|
|
67
94
|
}
|
|
68
95
|
const frame = requestAnimationFrame(measureOverlap);
|
|
69
96
|
return () => cancelAnimationFrame(frame);
|
|
70
97
|
}, [keyboard.isVisible, keyboard.height, keyboard.screenY, measureOverlap]);
|
|
71
98
|
|
|
99
|
+
const shouldAdjust =
|
|
100
|
+
enabled &&
|
|
101
|
+
keyboard.isVisible &&
|
|
102
|
+
behavior !== 'none' &&
|
|
103
|
+
behavior !== 'resize';
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!isAbsolutelyPositioned) {
|
|
106
|
+
animatedDisplacement.setValue(0);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const animation = Animated.timing(animatedDisplacement, {
|
|
110
|
+
toValue: shouldAdjust ? -keyboardDisplacement : 0,
|
|
111
|
+
duration: keyboard.animationDuration,
|
|
112
|
+
useNativeDriver: true,
|
|
113
|
+
});
|
|
114
|
+
animation.start();
|
|
115
|
+
return () => animation.stop();
|
|
116
|
+
}, [
|
|
117
|
+
animatedDisplacement,
|
|
118
|
+
isAbsolutelyPositioned,
|
|
119
|
+
keyboard.animationDuration,
|
|
120
|
+
keyboardDisplacement,
|
|
121
|
+
shouldAdjust,
|
|
122
|
+
]);
|
|
123
|
+
|
|
72
124
|
const handleLayout = useCallback(
|
|
73
125
|
(event: Parameters<NonNullable<typeof onLayout>>[0]) => {
|
|
74
126
|
onLayout?.(event);
|
|
@@ -76,30 +128,38 @@ export const KeyboardAwareView: React.ForwardRefExoticComponent<
|
|
|
76
128
|
},
|
|
77
129
|
[measureOverlap, onLayout],
|
|
78
130
|
);
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
behavior !== 'none' &&
|
|
83
|
-
behavior !== 'resize' &&
|
|
131
|
+
const adjustPadding =
|
|
132
|
+
shouldAdjust &&
|
|
133
|
+
!isAbsolutelyPositioned &&
|
|
84
134
|
keyboardOverlap > 0 &&
|
|
85
135
|
typeof flattenedStyle?.paddingBottom !== 'string';
|
|
136
|
+
const stickyTransform = isAbsolutelyPositioned
|
|
137
|
+
? [
|
|
138
|
+
...(Array.isArray(flattenedStyle?.transform)
|
|
139
|
+
? flattenedStyle.transform
|
|
140
|
+
: []),
|
|
141
|
+
{ translateY: animatedDisplacement },
|
|
142
|
+
]
|
|
143
|
+
: undefined;
|
|
86
144
|
|
|
87
145
|
return (
|
|
88
|
-
<View
|
|
146
|
+
<Animated.View
|
|
89
147
|
{...props}
|
|
90
148
|
ref={viewRef}
|
|
91
149
|
onLayout={handleLayout}
|
|
92
150
|
style={
|
|
93
|
-
|
|
151
|
+
adjustPadding
|
|
94
152
|
? [
|
|
95
153
|
style,
|
|
96
154
|
{
|
|
97
155
|
paddingBottom: Math.max(
|
|
98
156
|
basePadding,
|
|
99
|
-
keyboardOverlap +
|
|
157
|
+
keyboardOverlap + spacing,
|
|
100
158
|
),
|
|
101
159
|
},
|
|
102
160
|
]
|
|
161
|
+
: isAbsolutelyPositioned
|
|
162
|
+
? [style, { transform: stickyTransform }]
|
|
103
163
|
: style
|
|
104
164
|
}
|
|
105
165
|
/>
|
package/src/types/index.ts
CHANGED
|
@@ -52,7 +52,7 @@ export interface KeyboardAwareScrollViewProps extends ScrollViewProps {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export interface KeyboardAwareViewProps extends ViewProps {
|
|
55
|
-
/** Defaults to `smart
|
|
55
|
+
/** Defaults to `smart`; absolute-positioned views are moved above the keyboard. */
|
|
56
56
|
behavior?: KeyboardBehavior;
|
|
57
57
|
/** Extra space between the view content and the keyboard, in points. */
|
|
58
58
|
keyboardSpacing?: number;
|
|
@@ -13,6 +13,18 @@ export interface ScrollCalculation {
|
|
|
13
13
|
delta: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/** Return how far a bottom edge must move to clear the keyboard and spacing. */
|
|
17
|
+
export function calculateKeyboardDisplacement(
|
|
18
|
+
viewport: ScrollViewport,
|
|
19
|
+
keyboardTop: number,
|
|
20
|
+
spacing: number,
|
|
21
|
+
): number {
|
|
22
|
+
return Math.max(
|
|
23
|
+
0,
|
|
24
|
+
viewport.y + viewport.height - keyboardTop + Math.max(0, spacing),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
/** Return the part of the measured view actually covered by the keyboard. */
|
|
17
29
|
export function calculateKeyboardOverlap(
|
|
18
30
|
viewport: ScrollViewport,
|