react-native-keyboard-controller
Version:
Keyboard manager which works in identical way on both iOS and Android
92 lines (89 loc) • 3.22 kB
JavaScript
import { useCallback } from "react";
import { Platform } from "react-native";
import { scrollTo, useAnimatedReaction } from "react-native-reanimated";
import { IS_FABRIC } from "../../../architecture";
import { isScrollAtEnd, shouldShiftContent } from "../useChatKeyboard/helpers";
/**
* Hook that reacts to `extraContentPadding` changes and conditionally
* adjusts the scroll position using `scrollTo` on both iOS and Android.
*
* Padding extension (scrollable range) is handled externally via a
* `useDerivedValue` that sums keyboard padding + extra content padding.
* This hook only handles the scroll correction.
*
* @param options - Configuration and shared values.
* @example
* ```tsx
* useExtraContentPadding({ scrollViewRef, extraContentPadding, ... });
* ```
*/
function useExtraContentPadding(options) {
const {
scrollViewRef,
extraContentPadding,
keyboardPadding,
blankSpace,
scroll,
layout,
size,
contentOffsetY,
inverted,
keyboardLiftBehavior,
freeze
} = options;
const scrollToTarget = useCallback(target => {
"worklet";
if (contentOffsetY && IS_FABRIC) {
// eslint-disable-next-line react-compiler/react-compiler
contentOffsetY.value = target;
} else if (Platform.OS === "android") {
// Defer scrollTo so the animatedProps inset commit lands first;
// otherwise the native ScrollView clamps to the old range.
requestAnimationFrame(() => {
// check that view is still mounted and ref is actual
// otherwise it may lead to a crash
if (!scrollViewRef()) {
return;
}
scrollTo(scrollViewRef, 0, target, false);
});
} else {
scrollTo(scrollViewRef, 0, target, false);
}
}, [scrollViewRef, contentOffsetY]);
useAnimatedReaction(() => extraContentPadding.value, (current, previous) => {
if (freeze.value || previous === null) {
return;
}
const rawDelta = current - previous;
if (rawDelta === 0) {
return;
}
// Compute effective delta considering blankSpace floor
const previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous);
const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current);
const effectiveDelta = currentTotal - previousTotal;
if (effectiveDelta === 0) {
// blankSpace absorbed the change
return;
}
const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted);
// "persistent": scroll on grow, hold position on shrink (unless at end)
if (keyboardLiftBehavior === "persistent" && effectiveDelta < 0 && !atEnd) {
return;
}
if (!shouldShiftContent(keyboardLiftBehavior, atEnd)) {
return;
}
if (inverted) {
const target = Math.max(scroll.value - effectiveDelta, -currentTotal);
scrollToTarget(target);
} else {
const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, 0);
const target = Math.min(scroll.value + effectiveDelta, maxScroll);
scrollToTarget(target);
}
}, [inverted, keyboardLiftBehavior]);
}
export { useExtraContentPadding };
//# sourceMappingURL=index.js.map