Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {PropsWithChildren, useEffect, useMemo} from 'react';
import React, {PropsWithChildren, useEffect, useMemo, useState} from 'react';
import {StyleSheet} from 'react-native';
import {asBaseComponent, Constants} from '../../commons/new';
import {LogService} from '../../services';
Expand Down Expand Up @@ -84,6 +84,41 @@ const FloatingButton = (props: FloatingButtonProps) => {
isAndroidEdgeToEdge,
testID
} = props;
const [isMounted, setIsMounted] = useState(visible);
const [shouldShowFooter, setShouldShowFooter] = useState(visible);

useEffect(() => {
let showFrame: number | undefined;
let secondShowFrame: number | undefined;
let hideTimeout: ReturnType<typeof setTimeout> | undefined;

if (visible) {
setIsMounted(true);
setShouldShowFooter(false);
showFrame = requestAnimationFrame(() => {
secondShowFrame = requestAnimationFrame(() => {
setShouldShowFooter(true);
});
});
} else {
setShouldShowFooter(false);
hideTimeout = setTimeout(() => {
setIsMounted(false);
}, withoutAnimation ? 0 : duration);
}

return () => {
if (showFrame !== undefined) {
cancelAnimationFrame(showFrame);
}
if (secondShowFrame !== undefined) {
cancelAnimationFrame(secondShowFrame);
}
if (hideTimeout !== undefined) {
clearTimeout(hideTimeout);
}
};
}, [visible, duration, withoutAnimation]);

useEffect(() => {
// eslint-disable-next-line max-len
Expand All @@ -106,6 +141,9 @@ const FloatingButton = (props: FloatingButtonProps) => {
if (!button && !secondaryButton) {
return null;
}
if (!isMounted) {
return null;
}

const renderPrimaryButton = () => {
if (!button) {
Expand Down Expand Up @@ -156,7 +194,7 @@ const FloatingButton = (props: FloatingButtonProps) => {

return (
<ScreenFooter
visible={visible}
visible={shouldShowFooter}
layout={isHorizontal ? ScreenFooterLayouts.HORIZONTAL : ScreenFooterLayouts.VERTICAL}
backgroundType={hideBackgroundOverlay ? ScreenFooterBackgrounds.TRANSPARENT : ScreenFooterBackgrounds.FADING}
keyboardBehavior={hoisted ? KeyboardBehavior.HOISTED : KeyboardBehavior.STICKY}
Expand Down
48 changes: 43 additions & 5 deletions packages/react-native-ui-lib/src/components/screenFooter/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {LayoutChangeEvent, StyleSheet, ViewStyle} from 'react-native';
import Animated, {useAnimatedKeyboard, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import {Keyboard} from 'uilib-native';
Expand Down Expand Up @@ -56,11 +56,41 @@ const ScreenFooter = (props: ScreenFooterProps) => {
isStatusBarTranslucentAndroid: isAndroidEdgeToEdge
});
const [height, setHeight] = useState(0);
const visibilityTranslateY = useSharedValue(0);
const initialHiddenOffset = 9999;
const visibilityTranslateY = useSharedValue(visible ? 0 : initialHiddenOffset);
const pendingEntranceAnimation = useRef(!visible);

// Update visibility translation when visible or height changes
useEffect(() => {
visibilityTranslateY.value = withTiming(visible ? 0 : height, {duration: animationDuration});
let animationFrame: number | undefined;
const hiddenOffset = height > 0 ? height : initialHiddenOffset;

if (visible) {
if (height === 0) {
pendingEntranceAnimation.current = true;
visibilityTranslateY.value = initialHiddenOffset;
return;
}

if (pendingEntranceAnimation.current) {
pendingEntranceAnimation.current = false;
visibilityTranslateY.value = height;
animationFrame = requestAnimationFrame(() => {
visibilityTranslateY.value = withTiming(0, {duration: animationDuration});
});
} else {
visibilityTranslateY.value = withTiming(0, {duration: animationDuration});
}
} else {
pendingEntranceAnimation.current = true;
visibilityTranslateY.value = withTiming(hiddenOffset, {duration: animationDuration});
}

return () => {
if (animationFrame !== undefined) {
cancelAnimationFrame(animationFrame);
}
};
}, [visible, height, animationDuration, visibilityTranslateY]);

// Animated style for STICKY behavior (counters Android system offset + visibility)
Expand Down Expand Up @@ -237,11 +267,19 @@ const ScreenFooter = (props: ScreenFooterProps) => {
);
}, [renderBackground, testID, contentContainerStyle, childrenArray]);

const renderHoistedFooterContent = useCallback(() => {
return (
<Animated.View testID={testID} style={hoistedAnimatedStyle} pointerEvents={visible ? 'box-none' : 'none'}>
{renderFooterContent()}
</Animated.View>
);
}, [testID, hoistedAnimatedStyle, visible, renderFooterContent]);

if (keyboardBehavior === KeyboardBehavior.HOISTED) {
return (
<Animated.View style={[styles.container, hoistedAnimatedStyle]} pointerEvents={visible ? 'box-none' : 'none'}>
<Animated.View style={styles.container}>
<Keyboard.KeyboardAccessoryView
renderContent={renderFooterContent}
renderContent={renderHoistedFooterContent}
kbInputRef={undefined}
scrollBehavior={Keyboard.KeyboardAccessoryView.scrollBehaviors.FIXED_OFFSET}
useSafeArea={false}
Expand Down
Loading