react-native-keyboard-controller 1.21.3 → 1.21.4
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/android/src/fabric/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +2 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/listeners/WindowDimensionListener.kt +1 -0
- package/android/src/main/java/com/reactnativekeyboardcontroller/managers/KeyboardControllerViewManagerImpl.kt +5 -1
- package/android/src/main/java/com/reactnativekeyboardcontroller/modules/KeyboardControllerModuleImpl.kt +8 -0
- package/android/src/paper/java/com/reactnativekeyboardcontroller/KeyboardControllerModule.kt +2 -0
- package/ios/KeyboardControllerModule.mm +12 -0
- package/ios/observers/FocusedInputObserver.swift +9 -2
- package/ios/views/KeyboardExtenderContainerView.swift +14 -4
- package/lib/commonjs/bindings.js +4 -1
- package/lib/commonjs/bindings.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +47 -10
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/commonjs/components/KeyboardAwareScrollView/types.js.map +1 -1
- package/lib/commonjs/components/KeyboardToolbar/constants.js +2 -2
- package/lib/commonjs/components/KeyboardToolbar/constants.js.map +1 -1
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/constants.js +3 -1
- package/lib/commonjs/constants.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/NativeKeyboardController.js.map +1 -1
- package/lib/commonjs/types/module.js.map +1 -1
- package/lib/module/bindings.js +4 -1
- package/lib/module/bindings.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/index.js +48 -11
- package/lib/module/components/KeyboardAwareScrollView/index.js.map +1 -1
- package/lib/module/components/KeyboardAwareScrollView/types.js.map +1 -1
- package/lib/module/components/KeyboardToolbar/constants.js +2 -2
- package/lib/module/components/KeyboardToolbar/constants.js.map +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/constants.js +3 -0
- package/lib/module/constants.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/NativeKeyboardController.js.map +1 -1
- package/lib/module/types/module.js.map +1 -1
- package/lib/typescript/components/KeyboardAwareScrollView/index.d.ts +1 -0
- package/lib/typescript/components/KeyboardAwareScrollView/types.d.ts +10 -0
- package/lib/typescript/components/index.d.ts +1 -1
- package/lib/typescript/constants.d.ts +1 -0
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/specs/NativeKeyboardController.d.ts +3 -1
- package/lib/typescript/types/module.d.ts +3 -0
- package/package.json +1 -1
- package/src/bindings.ts +3 -0
- package/src/components/KeyboardAwareScrollView/index.tsx +79 -29
- package/src/components/KeyboardAwareScrollView/types.ts +11 -0
- package/src/components/KeyboardToolbar/constants.ts +2 -3
- package/src/components/index.ts +1 -0
- package/src/constants.ts +4 -0
- package/src/index.ts +1 -0
- package/src/specs/NativeKeyboardController.ts +3 -1
- package/src/types/module.ts +4 -0
package/src/bindings.ts
CHANGED
|
@@ -27,6 +27,9 @@ export const KeyboardControllerNative: KeyboardControllerNativeModule = {
|
|
|
27
27
|
Promise.resolve({ x: 0, y: 0, width: 0, height: 0 }),
|
|
28
28
|
addListener: NOOP,
|
|
29
29
|
removeListeners: NOOP,
|
|
30
|
+
getConstants: () => ({
|
|
31
|
+
keyboardBorderRadius: 0,
|
|
32
|
+
}),
|
|
30
33
|
};
|
|
31
34
|
/**
|
|
32
35
|
* An event emitter that provides a way to subscribe to next keyboard events:
|
|
@@ -12,6 +12,7 @@ import Reanimated, {
|
|
|
12
12
|
scrollTo,
|
|
13
13
|
useAnimatedReaction,
|
|
14
14
|
useAnimatedRef,
|
|
15
|
+
useAnimatedStyle,
|
|
15
16
|
useDerivedValue,
|
|
16
17
|
useSharedValue,
|
|
17
18
|
} from "react-native-reanimated";
|
|
@@ -104,6 +105,7 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
104
105
|
disableScrollOnKeyboardHide = false,
|
|
105
106
|
enabled = true,
|
|
106
107
|
extraKeyboardSpace = 0,
|
|
108
|
+
mode = "insets",
|
|
107
109
|
ScrollViewComponent = Reanimated.ScrollView,
|
|
108
110
|
snapToOffsets,
|
|
109
111
|
...rest
|
|
@@ -219,30 +221,39 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
219
221
|
},
|
|
220
222
|
[bottomOffset, enabled, height, snapToOffsets],
|
|
221
223
|
);
|
|
222
|
-
const removeGhostPadding = useCallback(
|
|
223
|
-
|
|
224
|
+
const removeGhostPadding = useCallback(
|
|
225
|
+
(e: number) => {
|
|
226
|
+
"worklet";
|
|
224
227
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
scrollViewAnimatedRef,
|
|
231
|
-
0,
|
|
232
|
-
scrollPosition.value -
|
|
233
|
-
interpolate(
|
|
234
|
-
e,
|
|
235
|
-
[initialKeyboardSize.value, keyboardHeight.value],
|
|
236
|
-
[ghostViewSpace.value, 0],
|
|
237
|
-
),
|
|
238
|
-
false,
|
|
239
|
-
);
|
|
228
|
+
// layout mode: the spacer view participates in layout, so the ScrollView
|
|
229
|
+
// reflows naturally when it shrinks — no manual scroll correction needed.
|
|
230
|
+
if (mode === "layout") {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
240
233
|
|
|
241
|
-
|
|
242
|
-
|
|
234
|
+
// insets mode: `ScrollViewWithBottomPadding` extends scrollable area without
|
|
235
|
+
// changing layout, so when the keyboard hides and we're at the end of the
|
|
236
|
+
// ScrollView we must manually scroll back.
|
|
237
|
+
if (!keyboardWillAppear.value && ghostViewSpace.value > 0) {
|
|
238
|
+
scrollTo(
|
|
239
|
+
scrollViewAnimatedRef,
|
|
240
|
+
0,
|
|
241
|
+
scrollPosition.value -
|
|
242
|
+
interpolate(
|
|
243
|
+
e,
|
|
244
|
+
[initialKeyboardSize.value, keyboardHeight.value],
|
|
245
|
+
[ghostViewSpace.value, 0],
|
|
246
|
+
),
|
|
247
|
+
false,
|
|
248
|
+
);
|
|
243
249
|
|
|
244
|
-
|
|
245
|
-
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return false;
|
|
254
|
+
},
|
|
255
|
+
[mode],
|
|
256
|
+
);
|
|
246
257
|
const performScrollWithPositionRestoration = useCallback(
|
|
247
258
|
(newPosition: number) => {
|
|
248
259
|
"worklet";
|
|
@@ -403,8 +414,14 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
403
414
|
scrollPosition.value = position.value;
|
|
404
415
|
// just persist height - later will be used in interpolation
|
|
405
416
|
keyboardHeight.value = e.height;
|
|
406
|
-
|
|
407
|
-
|
|
417
|
+
|
|
418
|
+
// insets mode: set the full contentInset upfront so that maybeScroll
|
|
419
|
+
// calculations are correct from the very first onMove frame.
|
|
420
|
+
// layout mode: do NOT set it here — the spacer must grow frame-by-frame
|
|
421
|
+
// in onMove to avoid a premature full-height jump before the keyboard moves.
|
|
422
|
+
if (mode === "insets") {
|
|
423
|
+
syncKeyboardFrame(e);
|
|
424
|
+
}
|
|
408
425
|
}
|
|
409
426
|
|
|
410
427
|
// focus was changed
|
|
@@ -445,13 +462,15 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
445
462
|
}
|
|
446
463
|
}
|
|
447
464
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
465
|
+
if (mode === "insets") {
|
|
466
|
+
ghostViewSpace.value =
|
|
467
|
+
position.value +
|
|
468
|
+
scrollViewLayout.value.height -
|
|
469
|
+
scrollViewContentSize.value.height;
|
|
452
470
|
|
|
453
|
-
|
|
454
|
-
|
|
471
|
+
if (ghostViewSpace.value > 0) {
|
|
472
|
+
scrollPosition.value = position.value;
|
|
473
|
+
}
|
|
455
474
|
}
|
|
456
475
|
},
|
|
457
476
|
onMove: (e) => {
|
|
@@ -461,6 +480,11 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
461
480
|
return;
|
|
462
481
|
}
|
|
463
482
|
|
|
483
|
+
// layout mode: drive the spacer view animation frame-by-frame
|
|
484
|
+
if (mode === "layout") {
|
|
485
|
+
syncKeyboardFrame(e);
|
|
486
|
+
}
|
|
487
|
+
|
|
464
488
|
// if the user has set disableScrollOnKeyboardHide, only auto-scroll when the keyboard opens
|
|
465
489
|
if (!disableScrollOnKeyboardHide || keyboardWillAppear.value) {
|
|
466
490
|
maybeScroll(e.height);
|
|
@@ -489,6 +513,7 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
489
513
|
},
|
|
490
514
|
},
|
|
491
515
|
[
|
|
516
|
+
mode,
|
|
492
517
|
maybeScroll,
|
|
493
518
|
removeGhostPadding,
|
|
494
519
|
disableScrollOnKeyboardHide,
|
|
@@ -555,6 +580,31 @@ const KeyboardAwareScrollView = forwardRef<
|
|
|
555
580
|
() => (enabled ? currentKeyboardFrameHeight.value : 0),
|
|
556
581
|
[enabled],
|
|
557
582
|
);
|
|
583
|
+
// layout mode only: a spacer view whose paddingBottom grows with the keyboard.
|
|
584
|
+
// The `+ 1` ensures the scroll view never reaches its absolute end during animation,
|
|
585
|
+
// avoiding the layout recalculation that triggers on every frame at the boundary.
|
|
586
|
+
// see: https://github.com/kirillzyusko/react-native-keyboard-controller/pull/342
|
|
587
|
+
const layoutSpacerStyle = useAnimatedStyle(
|
|
588
|
+
() =>
|
|
589
|
+
enabled && mode === "layout"
|
|
590
|
+
? { paddingBottom: currentKeyboardFrameHeight.value + 1 }
|
|
591
|
+
: {},
|
|
592
|
+
[enabled, mode],
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
if (mode === "layout") {
|
|
596
|
+
return (
|
|
597
|
+
<ScrollViewComponent
|
|
598
|
+
ref={onRef}
|
|
599
|
+
{...rest}
|
|
600
|
+
scrollEventThrottle={16}
|
|
601
|
+
onLayout={onScrollViewLayout}
|
|
602
|
+
>
|
|
603
|
+
{children}
|
|
604
|
+
{enabled && <Reanimated.View style={layoutSpacerStyle} />}
|
|
605
|
+
</ScrollViewComponent>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
558
608
|
|
|
559
609
|
return (
|
|
560
610
|
<ScrollViewWithBottomPadding
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AnimatedScrollViewComponent } from "../ScrollViewWithBottomPadding";
|
|
2
2
|
import type { ScrollView, ScrollViewProps } from "react-native";
|
|
3
3
|
|
|
4
|
+
export type KeyboardAwareScrollViewMode = "insets" | "layout";
|
|
5
|
+
|
|
4
6
|
export type KeyboardAwareScrollViewProps = {
|
|
5
7
|
/** The distance between the keyboard and the caret inside a focused `TextInput` when the keyboard is shown. Default is `0`. */
|
|
6
8
|
bottomOffset?: number;
|
|
@@ -10,6 +12,15 @@ export type KeyboardAwareScrollViewProps = {
|
|
|
10
12
|
enabled?: boolean;
|
|
11
13
|
/** Adjusting the bottom spacing of KeyboardAwareScrollView. Default is `0`. */
|
|
12
14
|
extraKeyboardSpace?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Controls how keyboard space is created at the bottom of the `ScrollView`.
|
|
17
|
+
*
|
|
18
|
+
* - `"insets"` *(default)*: Extends the scrollable area via `contentInset` (iOS) and `ClippingScrollView` (Android). No layout reflow occurs — content positions remain stable during keyboard animation. Recommended for most use cases.
|
|
19
|
+
* - `"layout"`: Appends a spacer `View` as the last child of the `ScrollView`. The spacer participates in layout, so flex-based arrangements (e.g. `justifyContent: "space-between"`, `gap`) reflow naturally when the keyboard appears. Use this when you need content to physically rearrange around the keyboard space.
|
|
20
|
+
*
|
|
21
|
+
* Default is `"insets"`.
|
|
22
|
+
*/
|
|
23
|
+
mode?: KeyboardAwareScrollViewMode;
|
|
13
24
|
/** Custom component for `ScrollView`. Default is `ScrollView`. */
|
|
14
25
|
ScrollViewComponent?: AnimatedScrollViewComponent;
|
|
15
26
|
} & ScrollViewProps;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KEYBOARD_BORDER_RADIUS } from "../../constants";
|
|
2
2
|
|
|
3
3
|
import type { HEX } from "./types";
|
|
4
4
|
|
|
@@ -10,6 +10,5 @@ export const TEST_ID_KEYBOARD_TOOLBAR_DONE = `${TEST_ID_KEYBOARD_TOOLBAR}.done`;
|
|
|
10
10
|
|
|
11
11
|
export const KEYBOARD_TOOLBAR_HEIGHT = 42;
|
|
12
12
|
export const DEFAULT_OPACITY: HEX = "FF";
|
|
13
|
-
export const KEYBOARD_HAS_ROUNDED_CORNERS =
|
|
14
|
-
Platform.OS === "ios" && parseInt(Platform.Version, 10) >= 26;
|
|
13
|
+
export const KEYBOARD_HAS_ROUNDED_CORNERS = KEYBOARD_BORDER_RADIUS > 0;
|
|
15
14
|
export const OPENED_OFFSET = KEYBOARD_HAS_ROUNDED_CORNERS ? -11 : 0;
|
package/src/components/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { default as KeyboardChatScrollView } from "./KeyboardChatScrollView";
|
|
|
9
9
|
export type { KeyboardAvoidingViewProps } from "./KeyboardAvoidingView";
|
|
10
10
|
export type { KeyboardStickyViewProps } from "./KeyboardStickyView";
|
|
11
11
|
export type {
|
|
12
|
+
KeyboardAwareScrollViewMode,
|
|
12
13
|
KeyboardAwareScrollViewProps,
|
|
13
14
|
KeyboardAwareScrollViewRef,
|
|
14
15
|
} from "./KeyboardAwareScrollView/types";
|
package/src/constants.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { KeyboardControllerNative } from "./bindings";
|
|
2
|
+
|
|
1
3
|
// copied from `android.view.WindowManager.LayoutParams`
|
|
2
4
|
export enum AndroidSoftInputModes {
|
|
3
5
|
SOFT_INPUT_ADJUST_NOTHING = 48,
|
|
@@ -17,3 +19,5 @@ export enum AndroidSoftInputModes {
|
|
|
17
19
|
SOFT_INPUT_STATE_UNSPECIFIED = 0,
|
|
18
20
|
SOFT_INPUT_STATE_VISIBLE = 4,
|
|
19
21
|
}
|
|
22
|
+
export const KEYBOARD_BORDER_RADIUS =
|
|
23
|
+
KeyboardControllerNative.getConstants().keyboardBorderRadius;
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { TurboModuleRegistry } from "react-native";
|
|
|
3
3
|
import type { TurboModule } from "react-native";
|
|
4
4
|
|
|
5
5
|
export interface Spec extends TurboModule {
|
|
6
|
-
readonly getConstants: () => {
|
|
6
|
+
readonly getConstants: () => {
|
|
7
|
+
keyboardBorderRadius: number;
|
|
8
|
+
};
|
|
7
9
|
|
|
8
10
|
// methods
|
|
9
11
|
setInputMode(mode: number): void;
|
package/src/types/module.ts
CHANGED
|
@@ -130,4 +130,8 @@ export type KeyboardControllerNativeModule = {
|
|
|
130
130
|
// native event module stuff
|
|
131
131
|
addListener: (eventName: string) => void;
|
|
132
132
|
removeListeners: (count: number) => void;
|
|
133
|
+
// constants
|
|
134
|
+
getConstants: () => {
|
|
135
|
+
keyboardBorderRadius: number;
|
|
136
|
+
};
|
|
133
137
|
};
|