"use client";
import { useEffect, useId } from "react";
import { MotionValue, motion, useSpring, useTransform, motionValue } from "motion/react";
import useMeasure from "react-use-measure";
const TRANSITION = {
type: "spring" as const,
stiffness: 280,
damping: 18,
mass: 0.3
};
// examples içinde motionlar eklenti olarak burada kalsın
// ama örnekler yani page.tsx dosyaları buradan çıktsın
function Digit({ value, place }: { value: number; place: number }) {
const valueRoundedToPlace = Math.floor(value / place) % 10;
const initial = motionValue(valueRoundedToPlace);
const animatedValue = useSpring(initial, TRANSITION);
useEffect(() => {
animatedValue.set(valueRoundedToPlace);
}, [animatedValue, valueRoundedToPlace]);
return (
<div className="relative inline-block w-[1ch] overflow-x-visible overflow-y-clip leading-none tabular-nums">
<div className="invisible">0</div>
{Array.from({ length: 10 }, (_, i) => (
<Number key={i} mv={animatedValue} number={i} />
))}
</div>
);
}
function Number({ mv, number }: { mv: MotionValue<number>; number: number }) {
const uniqueId = useId();
const [ref, bounds] = useMeasure();
const y = useTransform(mv, (latest) => {
if (!bounds.height) return 0;
const placeValue = latest % 10;
const offset = (10 + number - placeValue) % 10;
let memo = offset * bounds.height;
if (offset > 5) {
memo -= 10 * bounds.height;
}
return memo;
});
if (!bounds.height) {
return (
<span ref={ref} className="invisible absolute">
{number}
</span>
);
}
return (
<motion.span
style={{ y }}
layoutId={`${uniqueId}-${number}`}
className="absolute inset-0 flex items-center justify-center"
transition={TRANSITION}
ref={ref}>
{number}
</motion.span>
);
}
type SlidingNumberProps = {
value: number;
padStart?: boolean;
decimalSeparator?: string;
};
export function Countdown({ value, padStart = false, decimalSeparator = "." }: SlidingNumberProps) {
const absValue = Math.abs(value);
const [integerPart, decimalPart] = absValue.toString().split(".");
const integerValue = parseInt(integerPart, 10);
const paddedInteger = padStart && integerValue < 10 ? `0${integerPart}` : integerPart;
const integerDigits = paddedInteger.split("");
const integerPlaces = integerDigits.map((_, i) => Math.pow(10, integerDigits.length - i - 1));
return (
<div className="flex items-center">
{value < 0 && "-"}
{integerDigits.map((_, index) => (
<Digit
key={`pos-${integerPlaces[index]}`}
value={integerValue}
place={integerPlaces[index]}
/>
))}
{decimalPart && (
<>
<span>{decimalSeparator}</span>
{decimalPart.split("").map((_, index) => (
<Digit
key={`decimal-${index}`}
value={parseInt(decimalPart, 10)}
place={Math.pow(10, decimalPart.length - index - 1)}
/>
))}
</>
)}
</div>
);
}