phx-react
Version:
PHX REACT
59 lines • 2.88 kB
JavaScript
import React, { useEffect, useRef } from 'react';
const easeOutExpo = (t) => (t === 1 ? 1 : 1 - Math.pow(2, -10 * t));
export const PHXMiniProgressBar = ({ percent, isCompleted, className }) => {
const barRef = useRef(null);
const rafRef = useRef(null);
const startTimeRef = useRef(null);
const startValueRef = useRef(0);
useEffect(() => {
if (percent == null) {
if (barRef.current) {
barRef.current.style.transform = 'translateX(-100%)';
}
startValueRef.current = 0;
return;
}
const target = Math.max(0, Math.min(100, Number(percent)));
const duration = 1200;
startValueRef.current = startValueRef.current || 0;
const animate = (now) => {
if (!startTimeRef.current)
startTimeRef.current = now;
const elapsed = now - startTimeRef.current;
const progress = Math.min(elapsed / duration, 1);
const eased = easeOutExpo(progress);
const current = startValueRef.current + (target - startValueRef.current) * eased;
if (barRef.current) {
barRef.current.style.transform = `translateX(${current - 100}%)`;
}
if (progress < 1) {
rafRef.current = requestAnimationFrame(animate);
}
else {
if (barRef.current) {
barRef.current.style.transform = `translateX(${target - 100}%)`;
}
startValueRef.current = target;
startTimeRef.current = null;
}
};
rafRef.current = requestAnimationFrame(animate);
return () => {
if (rafRef.current)
cancelAnimationFrame(rafRef.current);
rafRef.current = null;
};
}, [percent]);
const displayPercent = percent == null ? '--' : `${Math.round(Number(percent))}%`;
return (React.createElement("div", { className: 'flex justify-start items-center pr-4' },
React.createElement("div", { className: 'flex items-center gap-3' },
React.createElement("div", { className: `flex-1 w-[91px] ${className}` },
React.createElement("div", { className: 'h-2 bg-gray-200 rounded-full overflow-hidden relative' },
React.createElement("div", { ref: barRef, className: `absolute inset-y-0 left-0 ${isCompleted ? 'bg-green-600' : 'bg-gray-900'} transition-colors duration-300`, style: {
transform: 'translateX(-100%)',
width: '100%',
} }))),
React.createElement("span", { className: 'text-sm font-medium text-left text-gray-500 w-[32px] tabular-nums' }, displayPercent))));
};
export default PHXMiniProgressBar;
//# sourceMappingURL=MiniProgressBar.js.map