UNPKG

@masumdev/rn-fab

Version:

A highly customizable Floating Action Button (FAB) component for React Native. Supports multiple variants including single, extended, stacked, clustered, and doted layouts. Built with smooth animations and optimized for both iOS and Android platforms.

159 lines (153 loc) 4.52 kB
"use strict"; import React, { useMemo } from 'react'; import { Image, Pressable, StyleSheet, TouchableOpacity } from 'react-native'; import Animated, { Easing, Extrapolation, interpolate, useAnimatedStyle, useDerivedValue, useSharedValue, withDelay, withSpring, withTiming } from 'react-native-reanimated'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const FabDoted = ({ items, style, containerStyle, theme = 'light', isOpen: setIsOpen, plusIcon }) => { const [firstValue, secondValue, thirdValue, isOpen] = [useSharedValue(30), useSharedValue(30), useSharedValue(30), useSharedValue(false)]; const progress = useDerivedValue(() => { 'worklet'; return isOpen.value ? withTiming(1) : withTiming(0); }); const config = useMemo(() => ({ easing: Easing.bezier(0.68, -0.6, 0.32, 1.6), duration: 500 }), []); const handlePress = () => { if (isOpen.value) { firstValue.value = withTiming(30, config); secondValue.value = withDelay(50, withTiming(30, config)); thirdValue.value = withDelay(100, withTiming(30, config)); } else { firstValue.value = withDelay(200, withSpring(110)); secondValue.value = withDelay(100, withSpring(100)); thirdValue.value = withSpring(110); } isOpen.value = !isOpen.value; setIsOpen?.(!isOpen.value); }; const firstAnimatedStyle = useAnimatedStyle(() => { 'worklet'; const scale = interpolate(firstValue.value, [30, 110], [0, 1], Extrapolation.CLAMP); return { right: firstValue.value, transform: [{ scale }] }; }); const secondAnimatedStyle = useAnimatedStyle(() => { 'worklet'; const scale = interpolate(secondValue.value, [30, 100], [0, 1], Extrapolation.CLAMP); return { bottom: secondValue.value, right: secondValue.value, transform: [{ scale }] }; }); const thirdAnimatedStyle = useAnimatedStyle(() => { 'worklet'; const scale = interpolate(thirdValue.value, [30, 110], [0, 1], Extrapolation.CLAMP); return { bottom: thirdValue.value, transform: [{ scale }] }; }); const plusAnimatedStyle = useAnimatedStyle(() => { 'worklet'; return { transform: [{ rotate: `${progress.value * 45}deg` }] }; }); const animatedStyles = useMemo(() => ({ first: firstAnimatedStyle, second: secondAnimatedStyle, third: thirdAnimatedStyle, plus: plusAnimatedStyle }), [firstAnimatedStyle, secondAnimatedStyle, thirdAnimatedStyle, plusAnimatedStyle]); const sampleItems = useMemo(() => [{ animate: animatedStyles.first }, { animate: animatedStyles.second }, { animate: animatedStyles.third }], [animatedStyles]); const backgroundStyle = theme === 'light' ? styles.lightBg : styles.darkBg; return /*#__PURE__*/_jsxs(Animated.View, { style: [styles.container, containerStyle], children: [items.map((item, index) => /*#__PURE__*/_jsx(Animated.View, { style: [styles.contentContainer, sampleItems[index].animate, backgroundStyle], children: /*#__PURE__*/_jsx(TouchableOpacity, { onPress: () => { item?.onPress?.(); handlePress(); }, style: [styles.iconContainer, animatedStyles.plus, style], children: item?.icon }) }, index)), /*#__PURE__*/_jsx(Pressable, { style: [styles.contentContainer, backgroundStyle], onPress: handlePress, children: /*#__PURE__*/_jsx(Animated.View, { style: [styles.iconContainer, animatedStyles.plus, style], children: plusIcon || /*#__PURE__*/_jsx(Image, { source: require('../assets/PlusIcon.png'), style: [styles.icon, { tintColor: theme === 'light' ? '#fff' : '#000' }] }) }) })] }); }; const styles = StyleSheet.create({ container: { flex: 1 }, contentContainer: { position: 'absolute', bottom: 30, right: 30, borderRadius: 50, // Add shadow shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5 }, iconContainer: { width: 60, height: 60, justifyContent: 'center', alignItems: 'center' }, icon: { width: 26, height: 26 }, lightBg: { backgroundColor: '#000' }, darkBg: { backgroundColor: '#fff' } }); export default /*#__PURE__*/React.memo(FabDoted); //# sourceMappingURL=FabDoted.js.map