vj-ui-components
Version:
A collection of beautiful, customizable React UI components including versatile navigation with dual layout support (sidebar/top), stylish input fields with icon support, advanced search with recommendations and autocomplete, elegant modals with animation
183 lines (168 loc) • 5.14 kB
JSX
import React, { useState, useEffect } from 'react';
import './Loader.css';
const Loader = ({
progress = null,
duration = 3000,
autoStart = true,
onComplete = () => {},
size = 'medium',
variant = 'circular',
showPercentage = true,
color = '#3b82f6',
children
}) => {
const [currentProgress, setCurrentProgress] = useState(progress !== null ? progress : 0);
const [isComplete, setIsComplete] = useState(false);
useEffect(() => {
if (progress !== null) {
setCurrentProgress(progress);
if (progress >= 100) {
setIsComplete(true);
onComplete();
}
return;
}
if (!autoStart) return;
const interval = setInterval(() => {
setCurrentProgress(prev => {
const next = prev + (100 / (duration / 100));
if (next >= 100) {
clearInterval(interval);
setIsComplete(true);
onComplete();
return 100;
}
return next;
});
}, 100);
return () => clearInterval(interval);
}, [progress, duration, autoStart, onComplete]);
const sizeClasses = {
small: 'loader-small',
medium: 'loader-medium',
large: 'loader-large'
};
const renderCircularLoader = () => {
const radius = 45;
const circumference = 2 * Math.PI * radius;
const strokeDashoffset = circumference - (currentProgress / 100) * circumference;
return (
<div className={`loader-container ${sizeClasses[size]}`}>
<div className="loader-circular">
<svg className="loader-svg" viewBox="0 0 100 100">
<circle
className="loader-track"
cx="50"
cy="50"
r={radius}
fill="none"
stroke="#e5e7eb"
strokeWidth="8"
/>
<circle
className="loader-progress"
cx="50"
cy="50"
r={radius}
fill="none"
stroke={color}
strokeWidth="8"
strokeLinecap="round"
strokeDasharray={circumference}
strokeDashoffset={strokeDashoffset}
style={{
transition: 'stroke-dashoffset 0.3s ease',
transformOrigin: '50% 50%',
transform: 'rotate(-90deg)'
}}
/>
</svg>
{showPercentage && (
<div className="loader-percentage" style={{ color }}>
{Math.round(currentProgress)}%
</div>
)}
</div>
{children && <div className="loader-content">{children}</div>}
</div>
);
};
const renderLinearLoader = () => (
<div className={`loader-container ${sizeClasses[size]}`}>
<div className="loader-linear">
<div className="loader-track-linear">
<div
className="loader-progress-linear"
style={{
width: `${currentProgress}%`,
backgroundColor: color,
transition: 'width 0.3s ease'
}}
/>
</div>
{showPercentage && (
<div className="loader-percentage-linear" style={{ color }}>
{Math.round(currentProgress)}%
</div>
)}
</div>
{children && <div className="loader-content">{children}</div>}
</div>
);
const renderSpinnerLoader = () => (
<div className={`loader-container ${sizeClasses[size]}`}>
<div className="loader-spinner">
<div
className="loader-spinner-ring"
style={{ borderTopColor: color }}
/>
{showPercentage && (
<div className="loader-percentage-spinner" style={{ color }}>
{Math.round(currentProgress)}%
</div>
)}
</div>
{children && <div className="loader-content">{children}</div>}
</div>
);
const renderDotsLoader = () => (
<div className={`loader-container ${sizeClasses[size]}`}>
<div className="loader-dots">
{[...Array(3)].map((_, i) => (
<div
key={i}
className="loader-dot"
style={{
backgroundColor: color,
animationDelay: `${i * 0.2}s`
}}
/>
))}
</div>
{showPercentage && (
<div className="loader-percentage-dots" style={{ color }}>
{Math.round(currentProgress)}%
</div>
)}
{children && <div className="loader-content">{children}</div>}
</div>
);
const renderLoader = () => {
switch (variant) {
case 'linear':
return renderLinearLoader();
case 'spinner':
return renderSpinnerLoader();
case 'dots':
return renderDotsLoader();
default:
return renderCircularLoader();
}
};
return (
<div className={`loader-wrapper ${isComplete ? 'loader-complete' : ''}`}>
{renderLoader()}
</div>
);
};
export default Loader;