diff --git a/lib/commonjs/components/KeyboardChatScrollView/index.js b/lib/commonjs/components/KeyboardChatScrollView/index.jsindex db8cfb1d289f91563f13c4dd842c783c99facc32..64c7e38ad8d9e866b15a53e561509e6b0ad33cb9 100644--- a/lib/commonjs/components/KeyboardChatScrollView/index.js+++ b/lib/commonjs/components/KeyboardChatScrollView/index.js@@ -26,9 +26,12 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE,+ adjustedInsetCompensation = 0,+ adjustedStartInsetCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp,+ onContentInsetChange, onEndVisible, ...rest }, ref) => {@@ -50,13 +53,17 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ freeze: freezeSV, offset, blankSpace,- extraContentPadding+ extraContentPadding,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation }); (0, _useExtraContentPadding.useExtraContentPadding)({ scrollViewRef, extraContentPadding, keyboardPadding: padding, blankSpace,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation, scroll, layout, size,@@ -82,10 +89,26 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ // a bug for you, please open an issue. const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); + // iOS applies the destination contentInset and contentOffset together at+ // keyboard-animation start. Keep that native target behavior, but report+ // the presentation height so virtualized-list layout follows the keyboard.+ const reportedPadding = (0, _reactNativeReanimated.useDerivedValue)(() => _reactNative.Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value);++ // Mirror the effective bottom padding (keyboard + composer + blank floor)+ // to the consumer - a virtualized list needs it in its own scroll math or+ // its end/maintain targets point at the under-the-keyboard resting offset.+ (0, _reactNativeReanimated.useAnimatedReaction)(() => reportedPadding.value, (current, previous) => {+ if (onContentInsetChange && current !== previous) {+ (0, _reactNativeReanimated.runOnJS)(onContentInsetChange)({+ bottom: current+ });+ }+ }, [onContentInsetChange]);+ // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). // Apps that render into the unsafe area can supply a negative // scrollIndicatorInsets adjustment at the application layer.- const indicatorPadding = (0, _reactNativeReanimated.useDerivedValue)(() => padding.value + extraContentPadding.value);+ const indicatorPadding = (0, _reactNativeReanimated.useDerivedValue)(() => padding.value); const onLayout = (0, _react.useCallback)(e => { onLayoutInternal(e); onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e);diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.jsindex 4d33b3fe426070cf312fc493ce902b2c24d853ea..e619708b95865afd8b43dd8f0a6523fcf67b1f53 100644--- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js+++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js@@ -206,7 +206,7 @@ const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentHeight, * ``` */ exports.clampedScrollTarget = clampedScrollTarget;-const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => {+const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { "worklet"; const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight;@@ -214,8 +214,9 @@ const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, const maxScroll = Math.max(contentHeight - layoutHeight, 0); return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); }- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0);- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll);+ const minScroll = -startInsetCompensation;+ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll);+ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); }; exports.computeIOSContentOffset = computeIOSContentOffset; //# sourceMappingURL=helpers.js.mapdiff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.jsindex 2073da84b8b2be291aa3181700c2b18a75d0fc56..7ae9bb220ce3cc23c788a79677afdc7d9547a27c 100644--- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js+++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js@@ -32,7 +32,9 @@ function useChatKeyboard(scrollViewRef, options) { freeze, offset, blankSpace,- extraContentPadding+ extraContentPadding,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation } = options; const padding = (0, _reactNativeReanimated.useSharedValue)(0); const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0);@@ -58,6 +60,9 @@ function useChatKeyboard(scrollViewRef, options) { targetKeyboardHeight.value = e.height; } const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);+ if (e.duration === 0) {+ currentHeight.value = effective;+ } const atEnd = (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted); // Scale minimum padding absorption by how much of it is visible.@@ -66,7 +71,7 @@ function useChatKeyboard(scrollViewRef, options) { const visiblePadding = visibleFraction * blankSpace.value; const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed);- const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);+ const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; // persistent mode: when keyboard shrinks, clamp to valid range if (keyboardLiftBehavior === "persistent" && effective < padding.value) {@@ -76,8 +81,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else {- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation);+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; }@@ -91,8 +96,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else {- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation);+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; }@@ -118,12 +123,23 @@ function useChatKeyboard(scrollViewRef, options) { contentOffsetY.value = scroll.value; return; }- contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding);+ contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation);+ },+ onMove: e => {+ "worklet";++ if (freeze.value) {+ return;+ }+ currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); },- onMove: () => {+ onInteractive: e => { "worklet"; - // iOS doesn't need per-frame updates (contentOffset handles it)+ if (freeze.value) {+ return;+ }+ currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); }, onEnd: e => { "worklet";@@ -132,9 +148,10 @@ function useChatKeyboard(scrollViewRef, options) { return; } const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset);+ currentHeight.value = effective; padding.value = effective; }- }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]);+ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); return { padding, currentHeight,diff --git a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.jsindex 0d50bdfeb7bbc5c14a31ed344f8a690e4fd340b3..e0b73653fd1dd23f0f2e2c85835a3d107e3f666d 100644--- a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js+++ b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js@@ -29,6 +29,8 @@ function useExtraContentPadding(options) { extraContentPadding, keyboardPadding, blankSpace,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation, scroll, layout, size,@@ -68,8 +70,8 @@ function useExtraContentPadding(options) { } // 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 previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation;+ const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { // blankSpace absorbed the change@@ -88,10 +90,11 @@ function useExtraContentPadding(options) { 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);+ const minScroll = -adjustedStartInsetCompensation;+ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll);+ const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); scrollToTarget(target); }- }, [inverted, keyboardLiftBehavior]);+ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); } //# sourceMappingURL=index.js.mapdiff --git a/lib/module/components/KeyboardChatScrollView/index.js b/lib/module/components/KeyboardChatScrollView/index.jsindex 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..76b858a6590ad0592cc663864ea822db55eaeceb 100644--- a/lib/module/components/KeyboardChatScrollView/index.js+++ b/lib/module/components/KeyboardChatScrollView/index.js@@ -1,7 +1,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import React, { forwardRef, useCallback, useMemo } from "react";-import { StyleSheet } from "react-native";-import { makeMutable, useAnimatedRef, useAnimatedStyle, useDerivedValue } from "react-native-reanimated";+import { Platform, StyleSheet } from "react-native";+import { makeMutable, runOnJS, useAnimatedReaction, useAnimatedRef, useAnimatedStyle, useDerivedValue } from "react-native-reanimated"; import Reanimated from "react-native-reanimated"; import useCombinedRef from "../hooks/useCombinedRef"; import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding";@@ -19,9 +19,12 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE,+ adjustedInsetCompensation = 0,+ adjustedStartInsetCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp,+ onContentInsetChange, onEndVisible, ...rest }, ref) => {@@ -43,13 +46,17 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ freeze: freezeSV, offset, blankSpace,- extraContentPadding+ extraContentPadding,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation }); useExtraContentPadding({ scrollViewRef, extraContentPadding, keyboardPadding: padding, blankSpace,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation, scroll, layout, size,@@ -75,10 +82,26 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ // a bug for you, please open an issue. const totalPadding = useDerivedValue(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); + // iOS applies the destination contentInset and contentOffset together at+ // keyboard-animation start. Keep that native target behavior, but report+ // the presentation height so virtualized-list layout follows the keyboard.+ const reportedPadding = useDerivedValue(() => Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value);++ // Mirror the effective bottom padding (keyboard + composer + blank floor)+ // to the consumer - a virtualized list needs it in its own scroll math or+ // its end/maintain targets point at the under-the-keyboard resting offset.+ useAnimatedReaction(() => reportedPadding.value, (current, previous) => {+ if (onContentInsetChange && current !== previous) {+ runOnJS(onContentInsetChange)({+ bottom: current+ });+ }+ }, [onContentInsetChange]);+ // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). // Apps that render into the unsafe area can supply a negative // scrollIndicatorInsets adjustment at the application layer.- const indicatorPadding = useDerivedValue(() => padding.value + extraContentPadding.value);+ const indicatorPadding = useDerivedValue(() => padding.value); const onLayout = useCallback(e => { onLayoutInternal(e); onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e);diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.jsindex 295e2219b560519b8d033968b5105d1da3e77315..ff7ee3fe0d880c2287a7b70d5ecf952e99357adf 100644--- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js+++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js@@ -193,7 +193,7 @@ export const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentH * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 * ``` */-export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => {+export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { "worklet"; const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight;@@ -201,7 +201,8 @@ export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentH const maxScroll = Math.max(contentHeight - layoutHeight, 0); return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); }- const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0);- return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll);+ const minScroll = -startInsetCompensation;+ const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll);+ return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); }; //# sourceMappingURL=helpers.js.mapdiff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.jsindex 52943c3a7d6a68fe2094dc1d112c07e6b9d890e4..cc4bfae859191aeb848179028b7217098cba2340 100644--- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js+++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js@@ -25,7 +25,9 @@ function useChatKeyboard(scrollViewRef, options) { freeze, offset, blankSpace,- extraContentPadding+ extraContentPadding,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation } = options; const padding = useSharedValue(0); const currentHeight = useSharedValue(0);@@ -51,6 +53,9 @@ function useChatKeyboard(scrollViewRef, options) { targetKeyboardHeight.value = e.height; } const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);+ if (e.duration === 0) {+ currentHeight.value = effective;+ } const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted); // Scale minimum padding absorption by how much of it is visible.@@ -59,7 +64,7 @@ function useChatKeyboard(scrollViewRef, options) { const visiblePadding = visibleFraction * blankSpace.value; const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed);- const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value);+ const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; // persistent mode: when keyboard shrinks, clamp to valid range if (keyboardLiftBehavior === "persistent" && effective < padding.value) {@@ -69,8 +74,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else {- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation);+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; }@@ -84,8 +89,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else {- const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0);- contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll));+ const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation);+ contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; }@@ -111,12 +116,23 @@ function useChatKeyboard(scrollViewRef, options) { contentOffsetY.value = scroll.value; return; }- contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding);+ contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation);+ },+ onMove: e => {+ "worklet";++ if (freeze.value) {+ return;+ }+ currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); },- onMove: () => {+ onInteractive: e => { "worklet"; - // iOS doesn't need per-frame updates (contentOffset handles it)+ if (freeze.value) {+ return;+ }+ currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); }, onEnd: e => { "worklet";@@ -125,9 +141,10 @@ function useChatKeyboard(scrollViewRef, options) { return; } const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset);+ currentHeight.value = effective; padding.value = effective; }- }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]);+ }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); return { padding, currentHeight,diff --git a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.jsindex 1afa50987a8d2a5fe3f36b20945efe804d48a873..7b05d0721c50e118cf78c07913b580a31293db77 100644--- a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js+++ b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js@@ -23,6 +23,8 @@ function useExtraContentPadding(options) { extraContentPadding, keyboardPadding, blankSpace,+ adjustedInsetCompensation,+ adjustedStartInsetCompensation, scroll, layout, size,@@ -62,8 +64,8 @@ function useExtraContentPadding(options) { } // 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 previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation;+ const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { // blankSpace absorbed the change@@ -82,11 +84,12 @@ function useExtraContentPadding(options) { 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);+ const minScroll = -adjustedStartInsetCompensation;+ const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll);+ const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); scrollToTarget(target); }- }, [inverted, keyboardLiftBehavior]);+ }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); } export { useExtraContentPadding }; //# sourceMappingURL=index.js.mapdiff --git a/lib/typescript/components/KeyboardChatScrollView/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/types.d.tsindex a036b431f03efb9d5379527c17db6fce62bfee09..5e56bf7edec9161698a745e8e6292bfccd68d3db 100644--- a/lib/typescript/components/KeyboardChatScrollView/types.d.ts+++ b/lib/typescript/components/KeyboardChatScrollView/types.d.ts@@ -86,6 +86,10 @@ export type KeyboardChatScrollViewProps = { * Default is `undefined` (equivalent to `0` — no minimum floor). */ blankSpace?: SharedValue<number>;+ /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */+ adjustedInsetCompensation?: number;+ /** Leading inset UIKit adds outside raw contentInset. Offset math only. */+ adjustedStartInsetCompensation?: number; /** * Fires whenever the effective content inset changes — the static `contentInset` * prop combined with the dynamic keyboard-driven padding.diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.tsindex 2b51cc4737b4a05b5396da31e67e54177f43c498..3acec1fbddee6227915590a6c8d449757cb4d7b2 100644--- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts+++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts@@ -129,4 +129,4 @@ export declare const clampedScrollTarget: (offsetBeforeScroll: number, keyboardH * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 * ``` */-export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number) => number;+export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number, startInsetCompensation?: number) => number;diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.tsindex aff9b5a8dbc2464546396437eaf6c5ae955b9f29..fdd83953271123be6caefaa786c469f0d92cce2b 100644--- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts+++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts@@ -9,6 +9,10 @@ type UseChatKeyboardOptions = { blankSpace: SharedValue<number>; /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ extraContentPadding: SharedValue<number>;+ /** Safe-area extra beyond raw contentInset. Offset math only. */+ adjustedInsetCompensation: number;+ /** Leading inset UIKit adds beyond the raw contentInset. Offset math only. */+ adjustedStartInsetCompensation: number; }; type UseChatKeyboardReturn = { /** Extra scrollable space (= keyboard height). Used as contentInset on iOS, contentInsetBottom/contentInsetTop on Android. */diff --git a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.tsindex ec73f70544a062fbecfc1d8be92d839d3e0bea6f..45b387446587fd3e611fa02a01534bb5d9fa6bbd 100644--- a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts+++ b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.tsLines 1–500 of 869. Load more
diff --git a/lib/commonjs/components/KeyboardChatScrollView/index.js b/lib/commonjs/components/KeyboardChatScrollView/index.js index db8cfb1d289f91563f13c4dd842c783c99facc32..64c7e38ad8d9e866b15a53e561509e6b0ad33cb9 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/index.js +++ b/lib/commonjs/components/KeyboardChatScrollView/index.js @@ -26,9 +26,12 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE, + adjustedInsetCompensation = 0, + adjustedStartInsetCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp, + onContentInsetChange, onEndVisible, ...rest }, ref) => { @@ -50,13 +53,17 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ freeze: freezeSV, offset, blankSpace, - extraContentPadding + extraContentPadding, + adjustedInsetCompensation, + adjustedStartInsetCompensation }); (0, _useExtraContentPadding.useExtraContentPadding)({ scrollViewRef, extraContentPadding, keyboardPadding: padding, blankSpace, + adjustedInsetCompensation, + adjustedStartInsetCompensation, scroll, layout, size, @@ -82,10 +89,26 @@ const KeyboardChatScrollView = /*#__PURE__*/(0, _react.forwardRef)(({ // a bug for you, please open an issue. const totalPadding = (0, _reactNativeReanimated.useDerivedValue)(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); + // iOS applies the destination contentInset and contentOffset together at + // keyboard-animation start. Keep that native target behavior, but report + // the presentation height so virtualized-list layout follows the keyboard. + const reportedPadding = (0, _reactNativeReanimated.useDerivedValue)(() => _reactNative.Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value); + + // Mirror the effective bottom padding (keyboard + composer + blank floor) + // to the consumer - a virtualized list needs it in its own scroll math or + // its end/maintain targets point at the under-the-keyboard resting offset. + (0, _reactNativeReanimated.useAnimatedReaction)(() => reportedPadding.value, (current, previous) => { + if (onContentInsetChange && current !== previous) { + (0, _reactNativeReanimated.runOnJS)(onContentInsetChange)({ + bottom: current + }); + } + }, [onContentInsetChange]); + // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). // Apps that render into the unsafe area can supply a negative // scrollIndicatorInsets adjustment at the application layer. - const indicatorPadding = (0, _reactNativeReanimated.useDerivedValue)(() => padding.value + extraContentPadding.value); + const indicatorPadding = (0, _reactNativeReanimated.useDerivedValue)(() => padding.value); const onLayout = (0, _react.useCallback)(e => { onLayoutInternal(e); onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js index 4d33b3fe426070cf312fc493ce902b2c24d853ea..e619708b95865afd8b43dd8f0a6523fcf67b1f53 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/helpers.js @@ -206,7 +206,7 @@ const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentHeight, * ``` */ exports.clampedScrollTarget = clampedScrollTarget; -const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { +const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { "worklet"; const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; @@ -214,8 +214,9 @@ const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, const maxScroll = Math.max(contentHeight - layoutHeight, 0); return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); } - const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); - return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); + const minScroll = -startInsetCompensation; + const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); + return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); }; exports.computeIOSContentOffset = computeIOSContentOffset; //# sourceMappingURL=helpers.js.map diff --git a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js index 2073da84b8b2be291aa3181700c2b18a75d0fc56..7ae9bb220ce3cc23c788a79677afdc7d9547a27c 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +++ b/lib/commonjs/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js @@ -32,7 +32,9 @@ function useChatKeyboard(scrollViewRef, options) { freeze, offset, blankSpace, - extraContentPadding + extraContentPadding, + adjustedInsetCompensation, + adjustedStartInsetCompensation } = options; const padding = (0, _reactNativeReanimated.useSharedValue)(0); const currentHeight = (0, _reactNativeReanimated.useSharedValue)(0); @@ -58,6 +60,9 @@ function useChatKeyboard(scrollViewRef, options) { targetKeyboardHeight.value = e.height; } const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); + if (e.duration === 0) { + currentHeight.value = effective; + } const atEnd = (0, _helpers.isScrollAtEnd)(scroll.value, layout.value.height, size.value.height, inverted); // Scale minimum padding absorption by how much of it is visible. @@ -66,7 +71,7 @@ function useChatKeyboard(scrollViewRef, options) { const visiblePadding = visibleFraction * blankSpace.value; const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); const scrollEffective = (0, _helpers.getScrollEffective)(effective, minimumPaddingAbsorbed); - const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value); + const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; // persistent mode: when keyboard shrinks, clamp to valid range if (keyboardLiftBehavior === "persistent" && effective < padding.value) { @@ -76,8 +81,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else { - const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); - contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); + const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); + contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; } @@ -91,8 +96,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else { - const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); - contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); + const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); + contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; } @@ -118,12 +123,23 @@ function useChatKeyboard(scrollViewRef, options) { contentOffsetY.value = scroll.value; return; } - contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); + contentOffsetY.value = (0, _helpers.computeIOSContentOffset)(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation); + }, + onMove: e => { + "worklet"; + + if (freeze.value) { + return; + } + currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); }, - onMove: () => { + onInteractive: e => { "worklet"; - // iOS doesn't need per-frame updates (contentOffset handles it) + if (freeze.value) { + return; + } + currentHeight.value = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); }, onEnd: e => { "worklet"; @@ -132,9 +148,10 @@ function useChatKeyboard(scrollViewRef, options) { return; } const effective = (0, _helpers.getEffectiveHeight)(e.height, targetKeyboardHeight.value, offset); + currentHeight.value = effective; padding.value = effective; } - }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); + }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); return { padding, currentHeight, diff --git a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js index 0d50bdfeb7bbc5c14a31ed344f8a690e4fd340b3..e0b73653fd1dd23f0f2e2c85835a3d107e3f666d 100644 --- a/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js +++ b/lib/commonjs/components/KeyboardChatScrollView/useExtraContentPadding/index.js @@ -29,6 +29,8 @@ function useExtraContentPadding(options) { extraContentPadding, keyboardPadding, blankSpace, + adjustedInsetCompensation, + adjustedStartInsetCompensation, scroll, layout, size, @@ -68,8 +70,8 @@ function useExtraContentPadding(options) { } // 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 previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation; + const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { // blankSpace absorbed the change @@ -88,10 +90,11 @@ function useExtraContentPadding(options) { 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); + const minScroll = -adjustedStartInsetCompensation; + const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); + const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); scrollToTarget(target); } - }, [inverted, keyboardLiftBehavior]); + }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); } //# sourceMappingURL=index.js.map diff --git a/lib/module/components/KeyboardChatScrollView/index.js b/lib/module/components/KeyboardChatScrollView/index.js index 612dd8bd9bd6cc3e30a5acac937ea3383eb1b630..76b858a6590ad0592cc663864ea822db55eaeceb 100644 --- a/lib/module/components/KeyboardChatScrollView/index.js +++ b/lib/module/components/KeyboardChatScrollView/index.js @@ -1,7 +1,7 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import React, { forwardRef, useCallback, useMemo } from "react"; -import { StyleSheet } from "react-native"; -import { makeMutable, useAnimatedRef, useAnimatedStyle, useDerivedValue } from "react-native-reanimated"; +import { Platform, StyleSheet } from "react-native"; +import { makeMutable, runOnJS, useAnimatedReaction, useAnimatedRef, useAnimatedStyle, useDerivedValue } from "react-native-reanimated"; import Reanimated from "react-native-reanimated"; import useCombinedRef from "../hooks/useCombinedRef"; import ScrollViewWithBottomPadding from "../ScrollViewWithBottomPadding"; @@ -19,9 +19,12 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ offset = 0, extraContentPadding = ZERO_CONTENT_PADDING, blankSpace = ZERO_BLANK_SPACE, + adjustedInsetCompensation = 0, + adjustedStartInsetCompensation = 0, applyWorkaroundForContentInsetHitTestBug = false, onLayout: onLayoutProp, onContentSizeChange: onContentSizeChangeProp, + onContentInsetChange, onEndVisible, ...rest }, ref) => { @@ -43,13 +46,17 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ freeze: freezeSV, offset, blankSpace, - extraContentPadding + extraContentPadding, + adjustedInsetCompensation, + adjustedStartInsetCompensation }); useExtraContentPadding({ scrollViewRef, extraContentPadding, keyboardPadding: padding, blankSpace, + adjustedInsetCompensation, + adjustedStartInsetCompensation, scroll, layout, size, @@ -75,10 +82,26 @@ const KeyboardChatScrollView = /*#__PURE__*/forwardRef(({ // a bug for you, please open an issue. const totalPadding = useDerivedValue(() => Math.min(layout.value.height, Math.max(blankSpace.value, padding.value + extraContentPadding.value))); + // iOS applies the destination contentInset and contentOffset together at + // keyboard-animation start. Keep that native target behavior, but report + // the presentation height so virtualized-list layout follows the keyboard. + const reportedPadding = useDerivedValue(() => Platform.OS === "ios" ? Math.min(layout.value.height, Math.max(blankSpace.value, currentHeight.value + extraContentPadding.value)) : totalPadding.value); + + // Mirror the effective bottom padding (keyboard + composer + blank floor) + // to the consumer - a virtualized list needs it in its own scroll math or + // its end/maintain targets point at the under-the-keyboard resting offset. + useAnimatedReaction(() => reportedPadding.value, (current, previous) => { + if (onContentInsetChange && current !== previous) { + runOnJS(onContentInsetChange)({ + bottom: current + }); + } + }, [onContentInsetChange]); + // Scroll indicator inset = keyboard + extraContentPadding (excludes blankSpace). // Apps that render into the unsafe area can supply a negative // scrollIndicatorInsets adjustment at the application layer. - const indicatorPadding = useDerivedValue(() => padding.value + extraContentPadding.value); + const indicatorPadding = useDerivedValue(() => padding.value); const onLayout = useCallback(e => { onLayoutInternal(e); onLayoutProp === null || onLayoutProp === void 0 || onLayoutProp(e); diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js index 295e2219b560519b8d033968b5105d1da3e77315..ff7ee3fe0d880c2287a7b70d5ecf952e99357adf 100644 --- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js +++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/helpers.js @@ -193,7 +193,7 @@ export const clampedScrollTarget = (offsetBeforeScroll, keyboardHeight, contentH * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 * ``` */ -export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll) => { +export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentHeight, layoutHeight, inverted, totalPaddingForMaxScroll, startInsetCompensation = 0) => { "worklet"; const paddingForMax = totalPaddingForMaxScroll !== undefined ? totalPaddingForMaxScroll : keyboardHeight; @@ -201,7 +201,8 @@ export const computeIOSContentOffset = (relativeScroll, keyboardHeight, contentH const maxScroll = Math.max(contentHeight - layoutHeight, 0); return Math.max(Math.min(relativeScroll - keyboardHeight, maxScroll), -paddingForMax); } - const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, 0); - return Math.min(Math.max(keyboardHeight + relativeScroll, 0), maxScroll); + const minScroll = -startInsetCompensation; + const maxScroll = Math.max(contentHeight - layoutHeight + paddingForMax, minScroll); + return Math.min(Math.max(keyboardHeight + relativeScroll, minScroll), maxScroll); }; //# sourceMappingURL=helpers.js.map diff --git a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js index 52943c3a7d6a68fe2094dc1d112c07e6b9d890e4..cc4bfae859191aeb848179028b7217098cba2340 100644 --- a/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js +++ b/lib/module/components/KeyboardChatScrollView/useChatKeyboard/index.ios.js @@ -25,7 +25,9 @@ function useChatKeyboard(scrollViewRef, options) { freeze, offset, blankSpace, - extraContentPadding + extraContentPadding, + adjustedInsetCompensation, + adjustedStartInsetCompensation } = options; const padding = useSharedValue(0); const currentHeight = useSharedValue(0); @@ -51,6 +53,9 @@ function useChatKeyboard(scrollViewRef, options) { targetKeyboardHeight.value = e.height; } const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); + if (e.duration === 0) { + currentHeight.value = effective; + } const atEnd = isScrollAtEnd(scroll.value, layout.value.height, size.value.height, inverted); // Scale minimum padding absorption by how much of it is visible. @@ -59,7 +64,7 @@ function useChatKeyboard(scrollViewRef, options) { const visiblePadding = visibleFraction * blankSpace.value; const minimumPaddingAbsorbed = Math.max(0, visiblePadding - extraContentPadding.value); const scrollEffective = getScrollEffective(effective, minimumPaddingAbsorbed); - const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value); + const actualTotalPadding = Math.max(blankSpace.value, effective + extraContentPadding.value) + adjustedInsetCompensation; // persistent mode: when keyboard shrinks, clamp to valid range if (keyboardLiftBehavior === "persistent" && effective < padding.value) { @@ -69,8 +74,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else { - const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); - contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); + const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); + contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; } @@ -84,8 +89,8 @@ function useChatKeyboard(scrollViewRef, options) { const maxScroll = Math.max(size.value.height - layout.value.height, 0); contentOffsetY.value = Math.max(-actualTotalPadding, Math.min(scroll.value, maxScroll)); } else { - const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, 0); - contentOffsetY.value = Math.max(0, Math.min(scroll.value, maxScroll)); + const maxScroll = Math.max(size.value.height - layout.value.height + actualTotalPadding, -adjustedStartInsetCompensation); + contentOffsetY.value = Math.max(-adjustedStartInsetCompensation, Math.min(scroll.value, maxScroll)); } return; } @@ -111,12 +116,23 @@ function useChatKeyboard(scrollViewRef, options) { contentOffsetY.value = scroll.value; return; } - contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding); + contentOffsetY.value = computeIOSContentOffset(relativeScroll, scrollEffective, size.value.height, layout.value.height, inverted, actualTotalPadding, adjustedStartInsetCompensation); + }, + onMove: e => { + "worklet"; + + if (freeze.value) { + return; + } + currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); }, - onMove: () => { + onInteractive: e => { "worklet"; - // iOS doesn't need per-frame updates (contentOffset handles it) + if (freeze.value) { + return; + } + currentHeight.value = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); }, onEnd: e => { "worklet"; @@ -125,9 +141,10 @@ function useChatKeyboard(scrollViewRef, options) { return; } const effective = getEffectiveHeight(e.height, targetKeyboardHeight.value, offset); + currentHeight.value = effective; padding.value = effective; } - }, [inverted, keyboardLiftBehavior, offset, extraContentPadding]); + }, [inverted, keyboardLiftBehavior, offset, extraContentPadding, adjustedInsetCompensation, adjustedStartInsetCompensation]); return { padding, currentHeight, diff --git a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js index 1afa50987a8d2a5fe3f36b20945efe804d48a873..7b05d0721c50e118cf78c07913b580a31293db77 100644 --- a/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js +++ b/lib/module/components/KeyboardChatScrollView/useExtraContentPadding/index.js @@ -23,6 +23,8 @@ function useExtraContentPadding(options) { extraContentPadding, keyboardPadding, blankSpace, + adjustedInsetCompensation, + adjustedStartInsetCompensation, scroll, layout, size, @@ -62,8 +64,8 @@ function useExtraContentPadding(options) { } // 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 previousTotal = Math.max(blankSpace.value, keyboardPadding.value + previous) + adjustedInsetCompensation; + const currentTotal = Math.max(blankSpace.value, keyboardPadding.value + current) + adjustedInsetCompensation; const effectiveDelta = currentTotal - previousTotal; if (effectiveDelta === 0) { // blankSpace absorbed the change @@ -82,11 +84,12 @@ function useExtraContentPadding(options) { 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); + const minScroll = -adjustedStartInsetCompensation; + const maxScroll = Math.max(size.value.height - layout.value.height + currentTotal, minScroll); + const target = Math.max(minScroll, Math.min(scroll.value + effectiveDelta, maxScroll)); scrollToTarget(target); } - }, [inverted, keyboardLiftBehavior]); + }, [inverted, keyboardLiftBehavior, adjustedInsetCompensation, adjustedStartInsetCompensation]); } export { useExtraContentPadding }; //# sourceMappingURL=index.js.map diff --git a/lib/typescript/components/KeyboardChatScrollView/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/types.d.ts index a036b431f03efb9d5379527c17db6fce62bfee09..5e56bf7edec9161698a745e8e6292bfccd68d3db 100644 --- a/lib/typescript/components/KeyboardChatScrollView/types.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/types.d.ts @@ -86,6 +86,10 @@ export type KeyboardChatScrollViewProps = { * Default is `undefined` (equivalent to `0` — no minimum floor). */ blankSpace?: SharedValue<number>; + /** Extra bottom inset UIKit adds beyond the raw contentInset (safe area). Offset math only. */ + adjustedInsetCompensation?: number; + /** Leading inset UIKit adds outside raw contentInset. Offset math only. */ + adjustedStartInsetCompensation?: number; /** * Fires whenever the effective content inset changes — the static `contentInset` * prop combined with the dynamic keyboard-driven padding. diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts index 2b51cc4737b4a05b5396da31e67e54177f43c498..3acec1fbddee6227915590a6c8d449757cb4d7b2 100644 --- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/helpers.d.ts @@ -129,4 +129,4 @@ export declare const clampedScrollTarget: (offsetBeforeScroll: number, keyboardH * computeIOSContentOffset(100, 300, 1000, 800, false); // 400 * ``` */ -export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number) => number; +export declare const computeIOSContentOffset: (relativeScroll: number, keyboardHeight: number, contentHeight: number, layoutHeight: number, inverted: boolean, totalPaddingForMaxScroll?: number, startInsetCompensation?: number) => number; diff --git a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts index aff9b5a8dbc2464546396437eaf6c5ae955b9f29..fdd83953271123be6caefaa786c469f0d92cce2b 100644 --- a/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/useChatKeyboard/types.d.ts @@ -9,6 +9,10 @@ type UseChatKeyboardOptions = { blankSpace: SharedValue<number>; /** Extra content padding shared value — needed on iOS to correctly clamp contentOffset. */ extraContentPadding: SharedValue<number>; + /** Safe-area extra beyond raw contentInset. Offset math only. */ + adjustedInsetCompensation: number; + /** Leading inset UIKit adds beyond the raw contentInset. Offset math only. */ + adjustedStartInsetCompensation: number; }; type UseChatKeyboardReturn = { /** Extra scrollable space (= keyboard height). Used as contentInset on iOS, contentInsetBottom/contentInsetTop on Android. */ diff --git a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts index ec73f70544a062fbecfc1d8be92d839d3e0bea6f..45b387446587fd3e611fa02a01534bb5d9fa6bbd 100644 --- a/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts +++ b/lib/typescript/components/KeyboardChatScrollView/useExtraContentPadding/index.d.ts