reactbits-mcp-server
Version:
MCP Server for React Bits - Access 99+ React components with animations, backgrounds, and UI elements
107 lines (86 loc) • 2.48 kB
JSX
import { useEffect, useRef } from "react";
import { useInView, useMotionValue, useSpring } from "framer-motion";
export default function CountUp({
to,
from = 0,
direction = "up",
delay = 0,
duration = 2,
className = "",
startWhen = true,
separator = "",
onStart,
onEnd,
}) {
const ref = useRef(null);
const motionValue = useMotionValue(direction === "down" ? to : from);
const damping = 20 + 40 * (1 / duration);
const stiffness = 100 * (1 / duration);
const springValue = useSpring(motionValue, {
damping,
stiffness,
});
const isInView = useInView(ref, { once: true, margin: "0px" });
const getDecimalPlaces = (num) => {
const str = num.toString();
if (str.includes(".")) {
const decimals = str.split(".")[1];
if (parseInt(decimals) !== 0) {
return decimals.length;
}
}
return 0;
};
const maxDecimals = Math.max(getDecimalPlaces(from), getDecimalPlaces(to));
useEffect(() => {
if (ref.current) {
ref.current.textContent = String(direction === "down" ? to : from);
}
}, [from, to, direction]);
useEffect(() => {
if (isInView && startWhen) {
if (typeof onStart === "function") onStart();
const timeoutId = setTimeout(() => {
motionValue.set(direction === "down" ? from : to);
}, delay * 1000);
const durationTimeoutId = setTimeout(() => {
if (typeof onEnd === "function") onEnd();
}, delay * 1000 + duration * 1000);
return () => {
clearTimeout(timeoutId);
clearTimeout(durationTimeoutId);
};
}
}, [
isInView,
startWhen,
motionValue,
direction,
from,
to,
delay,
onStart,
onEnd,
duration,
]);
useEffect(() => {
const unsubscribe = springValue.on("change", (latest) => {
if (ref.current) {
const hasDecimals = maxDecimals > 0;
const options = {
useGrouping: !!separator,
minimumFractionDigits: hasDecimals ? maxDecimals : 0,
maximumFractionDigits: hasDecimals ? maxDecimals : 0,
};
const formattedNumber = Intl.NumberFormat("en-US", options).format(
latest
);
ref.current.textContent = separator
? formattedNumber.replace(/,/g, separator)
: formattedNumber;
}
});
return () => unsubscribe();
}, [springValue, separator, maxDecimals]);
return <span className={className} ref={ref} />;
}