Files
musicland/src/components/AnimatedPaginationDot/AnimatedPaginationDot.js
T
2025-10-06 10:13:17 +02:00

137 lines
3.5 KiB
JavaScript

import React, { useMemo } from "react";
import { Animated, StyleSheet, useWindowDimensions, View } from "react-native";
import { Palette } from "../../styles";
const ORIENTATION = {
HORIZONTAL: "horizontal",
VERTICAL: "vertical",
};
const AnimatedPaginationDot = ({
data = [],
scrollValue,
itemDimension,
containerStyle,
dotStyle,
orientation = ORIENTATION.HORIZONTAL,
expandingDotSize = 20,
inactiveDotOpacity = 0.4,
inactiveDotColor = "rgba(255,255,255,0.4)",
activeDotColor = Palette.white,
baseDotSize = 10,
}) => {
const animatedValue = useMemo(
() => scrollValue || new Animated.Value(0),
[scrollValue],
);
const { width, height } = useWindowDimensions();
const distance = useMemo(() => {
if (typeof itemDimension === "number" && itemDimension > 0) {
return itemDimension;
}
if (orientation === ORIENTATION.VERTICAL) {
return Math.max(height, 1);
}
return Math.max(width, 1);
}, [height, itemDimension, orientation, width]);
const resolvedDotStyle = useMemo(() => StyleSheet.flatten(dotStyle) || {}, [dotStyle]);
const defaultSize = Math.max(baseDotSize, 1);
const baseWidth =
typeof resolvedDotStyle?.width === "number"
? resolvedDotStyle.width
: defaultSize;
const baseHeight =
typeof resolvedDotStyle?.height === "number"
? resolvedDotStyle.height
: defaultSize;
const containerOrientationStyle =
orientation === ORIENTATION.VERTICAL
? styles.containerVertical
: styles.containerHorizontal;
return (
<View
pointerEvents="none"
style={[styles.containerBase, containerOrientationStyle, containerStyle]}
>
{data.map((_, index) => {
const inputRange = [
(index - 1) * distance,
index * distance,
(index + 1) * distance,
];
const staticSizeStyle = {
width: baseWidth,
height: baseHeight,
};
const color = animatedValue.interpolate({
inputRange,
outputRange: [inactiveDotColor, activeDotColor, inactiveDotColor],
extrapolate: "clamp",
});
const opacity = animatedValue.interpolate({
inputRange,
outputRange: [inactiveDotOpacity, 1, inactiveDotOpacity],
extrapolate: "clamp",
});
const primarySize = animatedValue.interpolate({
inputRange,
outputRange: [
orientation === ORIENTATION.VERTICAL ? baseHeight : baseWidth,
expandingDotSize,
orientation === ORIENTATION.VERTICAL ? baseHeight : baseWidth,
],
extrapolate: "clamp",
});
const animatedSizeStyle =
orientation === ORIENTATION.VERTICAL
? { height: primarySize }
: { width: primarySize };
return (
<Animated.View
key={`pagination-dot-${index}`}
style={[
styles.dotBase,
staticSizeStyle,
dotStyle,
animatedSizeStyle,
{ backgroundColor: color, opacity },
]}
/>
);
})}
</View>
);
};
AnimatedPaginationDot.orientation = ORIENTATION;
export default AnimatedPaginationDot;
const styles = StyleSheet.create({
containerBase: {
alignItems: "center",
justifyContent: "center",
},
containerHorizontal: {
flexDirection: "row",
},
containerVertical: {
flexDirection: "column",
},
dotBase: {
borderRadius: 999,
marginHorizontal: 4,
marginVertical: 4,
},
});