@mindinventory/react-native-tab-bar-interaction
Version:
A tabbar component for React Native
219 lines (217 loc) • 9.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getPathXCenterByIndex = exports.getPathXCenter = exports.TabBar = void 0;
var _react = require("react");
var _reactNative = require("react-native");
var _d3Shape = require("d3-shape");
var _reactNativeRedash = require("react-native-redash");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
var _TabItem = require("./TabItem.js");
var _AnimatedCircle = _interopRequireDefault(require("./AnimatedCircle.js"));
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
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 AnimatedPath = _reactNativeReanimated.default.createAnimatedComponent(_reactNativeSvg.Path);
const TRANSITION_SPEED = 300;
const FIX_WIDTH = 380;
const TAB_BAR_HEIGHT = 64;
const generateTabShapePath = (position, adjustedHeight, tabContainerWidth, numOfTabs) => {
const adjustedWidth = tabContainerWidth / numOfTabs;
const tabX = adjustedWidth * position;
const scaleGen = tabContainerWidth / FIX_WIDTH * 1;
const SCALE = Number(scaleGen.toFixed(2));
const radius = adjustedHeight / 1.6 * SCALE; // Increase radius slightly
const lineGenerator = (0, _d3Shape.line)().curve(_d3Shape.curveBasis);
const halfCircle = lineGenerator([[tabX - radius, 0],
// Start of left side of half-circle
[tabX - radius, radius / 1.2],
// Control point for smoother, deeper transition
[tabX, radius * 1.4],
// Higher point at the top for more depth
[tabX + radius, radius / 1.2],
// Control point for smoother, deeper transition
[tabX + radius, 0] // End of right side of half-circle
]);
return `${halfCircle}`;
};
const getPathXCenter = currentPath => {
const curves = (0, _reactNativeRedash.parse)(currentPath).curves;
const startPoint = curves[0].to;
const endPoint = curves[curves.length - 1].to;
const centerX = (startPoint.x + endPoint.x) / 2;
return centerX;
};
exports.getPathXCenter = getPathXCenter;
const getPathXCenterByIndex = (tabPaths, index) => {
const curves = tabPaths[index].curves;
const startPoint = curves[0].to;
const endPoint = curves[curves.length - 1].to;
const centerX = (startPoint.x + endPoint.x) / 2;
return centerX;
};
exports.getPathXCenterByIndex = getPathXCenterByIndex;
const TabBar = props => {
const {
tabs,
onTabChange,
containerWidth,
tabBarContainerBackground,
circleFillColor,
containerBottomSpace,
containerTopRightRadius = 10,
containerTopLeftRadius = 10,
containerBottomLeftRadius = 25,
containerBottomRightRadius = 25,
defaultActiveTabIndex,
transitionSpeed
} = props;
const [currentIndex, setCurrentIndex] = (0, _react.useState)(0);
const numOfTabs = (0, _react.useMemo)(() => tabs.length > 2 ? tabs.length : 3, [tabs.length]);
const containerPath = (0, _react.useMemo)(() => {
// return `M0,0L${containerWidth},0L${containerWidth},0L${containerWidth},${TAB_BAR_HEIGHT}L0,${TAB_BAR_HEIGHT}L0,0`;
return `M${containerTopLeftRadius},0
H${containerWidth - containerTopRightRadius}
A${containerTopRightRadius},${containerTopRightRadius} 0 0 1 ${containerWidth},${containerTopRightRadius}
V${TAB_BAR_HEIGHT - containerBottomRightRadius}
A${containerBottomRightRadius},${containerBottomRightRadius} 0 0 1 ${containerWidth - containerBottomRightRadius},${TAB_BAR_HEIGHT}
H${containerBottomLeftRadius}
A${containerBottomLeftRadius},${containerBottomLeftRadius} 0 0 1 0,${TAB_BAR_HEIGHT - containerBottomLeftRadius}
V${containerTopLeftRadius}
A${containerTopLeftRadius},${containerTopLeftRadius} 0 0 1 ${containerTopLeftRadius},0
Z`;
}, [containerBottomLeftRadius, containerBottomRightRadius, containerTopLeftRadius, containerTopRightRadius, containerWidth]);
const curvedPaths = (0, _react.useMemo)(() => {
return Array.from({
length: numOfTabs
}, (_, index) => {
const tabShapePath = generateTabShapePath(index + 0.5, TAB_BAR_HEIGHT, containerWidth, numOfTabs);
return (0, _reactNativeRedash.parse)(`${tabShapePath}`);
});
}, [containerWidth, numOfTabs]);
const circleXCoordinate = (0, _reactNativeReanimated.useSharedValue)(0);
const progress = (0, _reactNativeReanimated.useSharedValue)(1);
const handleMoveCircle = currentPath => {
circleXCoordinate.value = getPathXCenter(currentPath);
};
const animatedProps = (0, _reactNativeReanimated.useAnimatedProps)(() => {
const currentPath = (0, _reactNativeRedash.interpolatePath)(progress.value, Array.from({
length: curvedPaths.length
}, (_, index) => index + 1), curvedPaths);
(0, _reactNativeReanimated.runOnJS)(handleMoveCircle)(currentPath);
return {
d: `${containerPath} ${currentPath}`
};
});
const handleTabPress = index => {
progress.value = (0, _reactNativeReanimated.withTiming)(index + 1, {
duration: transitionSpeed ? transitionSpeed : TRANSITION_SPEED,
easing: _reactNativeReanimated.Easing.linear
});
setCurrentIndex(index);
};
(0, _react.useEffect)(() => {
if (defaultActiveTabIndex !== undefined) {
progress.value = (0, _reactNativeReanimated.withTiming)(defaultActiveTabIndex + 1, {
duration: transitionSpeed ? transitionSpeed : TRANSITION_SPEED
});
setCurrentIndex(defaultActiveTabIndex);
}
}, [defaultActiveTabIndex, progress, transitionSpeed]);
const tabBarContainerStyle = (0, _react.useMemo)(() => {
return {
bottom: containerBottomSpace ? containerBottomSpace : 0,
borderBottomLeftRadius: 10,
borderBottomRightRadius: 10
};
}, [containerBottomSpace]);
if (tabs.length > 5) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: styles.emptyContainer,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
children: "You can add maximum five tabs"
})
});
} else if (tabs.length < 2) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: styles.emptyContainer,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
children: "Please add tab data"
})
});
}
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
style: [styles.tabBarContainer, tabBarContainerStyle],
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.default, {
width: containerWidth,
height: TAB_BAR_HEIGHT,
style: styles.shadowMd,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(AnimatedPath, {
fill: tabBarContainerBackground ? tabBarContainerBackground : '#fff',
animatedProps: animatedProps
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_AnimatedCircle.default, {
circleX: circleXCoordinate,
circleFillColor: circleFillColor
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: [styles.tabItemsContainer, {
height: TAB_BAR_HEIGHT
}],
children: tabs.map((val, index) => {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_TabItem.TabItem, {
label: val.name,
activeIcon: val.activeIcon,
inactiveIcon: val.inactiveIcon,
activeIndex: defaultActiveTabIndex ? defaultActiveTabIndex : 1,
index: index,
onTabPress: () => {
handleTabPress(index);
if (val !== undefined) {
onTabChange(val, index);
}
},
containerWidth: containerWidth,
curvedPaths: curvedPaths,
currentIndex: currentIndex,
transitionSpeed: transitionSpeed
}, index.toString());
})
})]
});
};
exports.TabBar = TabBar;
const styles = _reactNative.StyleSheet.create({
tabBarContainer: {
position: 'absolute',
zIndex: 2,
marginHorizontal: 'auto',
alignSelf: 'center',
bottom: 0,
borderBottomLeftRadius: 30
},
tabItemsContainer: {
position: 'absolute',
flexDirection: 'row',
width: '100%',
overflow: 'hidden'
},
shadowMd: {
elevation: 3,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 3,
shadowOffset: {
width: 0,
height: 3
}
},
emptyContainer: {
justifyContent: 'center',
alignItems: 'center',
height: TAB_BAR_HEIGHT
}
});
//# sourceMappingURL=TabBar.js.map