@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.
167 lines (160 loc) • 6.36 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const FabStacked = ({
items,
style,
containerStyle,
theme = 'light',
isOpen: setIsOpen,
plusIcon
}) => {
// Consolidate initial values
const [firstValue, secondValue, thirdValue, isOpen] = [(0, _reactNativeReanimated.useSharedValue)(30), (0, _reactNativeReanimated.useSharedValue)(30), (0, _reactNativeReanimated.useSharedValue)(30), (0, _reactNativeReanimated.useSharedValue)(false)];
const progress = (0, _reactNativeReanimated.useDerivedValue)(() => {
'worklet';
return isOpen.value ? (0, _reactNativeReanimated.withTiming)(1) : (0, _reactNativeReanimated.withTiming)(0);
});
const config = (0, _react.useMemo)(() => ({
easing: _reactNativeReanimated.Easing.bezier(0.68, -0.6, 0.32, 1.6),
duration: 500
}), []);
const handlePress = () => {
if (isOpen.value) {
firstValue.value = (0, _reactNativeReanimated.withTiming)(30, config);
secondValue.value = (0, _reactNativeReanimated.withDelay)(50, (0, _reactNativeReanimated.withTiming)(30, config));
thirdValue.value = (0, _reactNativeReanimated.withDelay)(100, (0, _reactNativeReanimated.withTiming)(30, config));
} else {
firstValue.value = (0, _reactNativeReanimated.withDelay)(200, (0, _reactNativeReanimated.withSpring)(130));
secondValue.value = (0, _reactNativeReanimated.withDelay)(100, (0, _reactNativeReanimated.withSpring)(210));
thirdValue.value = (0, _reactNativeReanimated.withSpring)(290);
}
isOpen.value = !isOpen.value;
setIsOpen?.(!isOpen.value);
};
// Consolidate animated styles
const firstAnimatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
'worklet';
const scale = (0, _reactNativeReanimated.interpolate)(firstValue.value, [30, 130], [0, 1], _reactNativeReanimated.Extrapolation.CLAMP);
return {
bottom: firstValue.value,
transform: [{
scale
}]
};
});
const secondAnimatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
'worklet';
const scale = (0, _reactNativeReanimated.interpolate)(secondValue.value, [30, 210], [0, 1], _reactNativeReanimated.Extrapolation.CLAMP);
return {
bottom: secondValue.value,
transform: [{
scale
}]
};
});
const thirdAnimatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
'worklet';
const scale = (0, _reactNativeReanimated.interpolate)(thirdValue.value, [30, 290], [0, 1], _reactNativeReanimated.Extrapolation.CLAMP);
return {
bottom: thirdValue.value,
transform: [{
scale
}]
};
});
const plusAnimatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
'worklet';
return {
transform: [{
rotate: `${progress.value * 45}deg`
}]
};
});
const animatedStyles = (0, _react.useMemo)(() => ({
first: firstAnimatedStyle,
second: secondAnimatedStyle,
third: thirdAnimatedStyle,
plus: plusAnimatedStyle
}), [firstAnimatedStyle, secondAnimatedStyle, thirdAnimatedStyle, plusAnimatedStyle]);
const sampleItems = (0, _react.useMemo)(() => [{
animate: animatedStyles.first
}, {
animate: animatedStyles.second
}, {
animate: animatedStyles.third
}], [animatedStyles]);
const backgroundStyle = theme === 'light' ? styles.lightBg : styles.darkBg;
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
style: [styles.container, containerStyle],
children: [items.map((item, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: [styles.contentContainer, sampleItems[index].animate, backgroundStyle],
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
onPress: () => {
item?.onPress?.();
handlePress();
},
style: [styles.iconContainer, animatedStyles.plus, style],
children: item?.icon
})
}, index)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
style: [styles.contentContainer, backgroundStyle],
onPress: handlePress,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
style: [styles.iconContainer, animatedStyles.plus, style],
children: plusIcon || /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
source: require('../assets/PlusIcon.png'),
style: [styles.icon, {
tintColor: theme === 'light' ? '#fff' : '#000'
}]
})
})
})]
});
};
const styles = _reactNative.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'
}
});
var _default = exports.default = /*#__PURE__*/_react.default.memo(FabStacked);
//# sourceMappingURL=FabStacked.js.map