import { default as React, useContext, useEffect, useRef, useState, } from 'react'; import { Animated, LayoutChangeEvent, StyleSheet, TouchableOpacity, View, } from 'react-native'; import { ApplicationContext } from '../Context'; import { Icon } from '../Icon'; export interface FloatingButtonProps { label?: string; onPress?: () => void; position?: 'center' | 'right'; icon?: string; iconColor?: string; size?: 'small' | 'large'; animatedValue?: Animated.Value; bottom?: number; renderComponent?: () => React.ReactNode; } export const FloatingButton: React.FC = ({ renderComponent, label = '', onPress = () => {}, position = 'right', icon = '', iconColor = '', size = 'small', animatedValue, bottom = 12, }: FloatingButtonProps) => { const { theme } = useContext(ApplicationContext); const maxWidth = useRef(0); const minWidth = size === 'small' ? 36 : 48; const [opacityAnimated] = useState(new Animated.Value(0)); // Initial opacity set to 0 const [widthAnimated, setWidthAnimated] = useState(); const lastOffset = useRef(0); const lastDirection = useRef(null); const [showText, setShowText] = React.useState(true); useEffect(() => { if (!label) return; Animated.timing(opacityAnimated, { toValue: 1, duration: 100, useNativeDriver: true, }).start(); const listener = animatedValue?.addListener(({ value }) => { if (value !== lastOffset.current && value > 0) { const direction = value > lastOffset.current ? 'down' : 'up'; lastOffset.current = value; if (lastDirection.current !== direction) { lastDirection.current = direction; if (direction === 'down') { Animated.timing(opacityAnimated, { toValue: 0, duration: 100, useNativeDriver: true, }).start(); Animated.timing(widthAnimated!, { toValue: minWidth, duration: 100, useNativeDriver: false, }).start(() => setShowText(false)); } else { Animated.timing(opacityAnimated, { toValue: 1, duration: 100, useNativeDriver: true, }).start(); Animated.timing(widthAnimated!, { toValue: maxWidth.current, duration: 100, useNativeDriver: false, }).start(() => setShowText(true)); } } } }); return () => { if (listener) { animatedValue?.removeListener(listener); } }; }, [animatedValue, label, minWidth, opacityAnimated, widthAnimated]); const handleLayout = (event: LayoutChangeEvent) => { const layout = event.nativeEvent.layout; if (widthAnimated) return; maxWidth.current = layout.width; setWidthAnimated(new Animated.Value(layout.width)); }; if (renderComponent) { return ( {renderComponent()} ); } return ( {showText && !!label && ( {label} )} ); }; const styles = StyleSheet.create({ container: { flexDirection: 'row', alignContent: 'center', alignItems: 'center', position: 'absolute', borderRadius: 100, paddingHorizontal: 12, flexWrap: 'wrap', alignSelf: 'flex-end', }, label: { marginLeft: 8, fontSize: 16, lineHeight: 22, fontWeight: '700', }, });