infoalloggi-monorepo/apps/infoalloggi/src/components/custom_ui/animated-number.tsx
2025-08-29 16:18:32 +02:00

56 lines
1.2 KiB
TypeScript

"use client";
import {
type MotionValue,
motion,
useSpring,
useTransform,
} from "framer-motion";
import { useEffect } from "react";
//DOC: https://www.cult-ui.com/docs/components/animated-number
interface AnimatedNumberProps {
value: number;
mass?: number;
stiffness?: number;
damping?: number;
precision?: number;
format?: (value: number) => string;
onAnimationStart?: () => void;
onAnimationComplete?: () => void;
}
export function AnimatedNumber({
value,
mass = 0.8,
stiffness = 75,
damping = 15,
precision = 0,
format,
onAnimationStart,
onAnimationComplete,
}: AnimatedNumberProps) {
const spring = useSpring(value, {
damping,
mass,
stiffness,
});
const display: MotionValue<string> = useTransform(spring, (current) =>
format
? format(parseFloat(current.toFixed(precision)))
: parseFloat(current.toFixed(precision)).toLocaleString(),
);
useEffect(() => {
spring.set(value);
if (onAnimationStart) onAnimationStart();
const unsubscribe = spring.on("change", () => {
if (spring.get() === value && onAnimationComplete) onAnimationComplete();
});
return () => unsubscribe();
}, [spring, value, onAnimationStart, onAnimationComplete]);
return <motion.span>{display}</motion.span>;
}