This repository has been archived on 2025-11-17. You can view files and clone it, but cannot push or open issues or pull requests.
Files
pypages/static/components/LoadingProgress.js
2025-11-16 18:01:30 +01:00

159 lines
4.2 KiB
JavaScript

// Loading Progress Component with Qt-style progress bar
export class LoadingProgress {
constructor(container) {
this.container = container;
this.progressBar = null;
this.progressFill = null;
this.animationFrame = null;
this.progress = 0;
this.targetProgress = 0;
this.isAnimating = false;
}
show(message = 'Loading...', showProgress = true) {
if (!this.container) return;
const loadingHTML = `
<div class="loading-container">
<div class="loading-spinner">
<div class="spinner-ring"></div>
<div class="spinner-ring"></div>
<div class="spinner-ring"></div>
<div class="spinner-ring"></div>
</div>
<div class="loading-text">${message}</div>
${showProgress ? `
<div class="progress-container">
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
<div class="progress-shine"></div>
</div>
<div class="progress-text" id="progressText">0%</div>
</div>
` : ''}
</div>
`;
this.container.innerHTML = loadingHTML;
if (showProgress) {
this.progressBar = this.container.querySelector('.progress-bar');
this.progressFill = document.getElementById('progressFill');
this.progressText = document.getElementById('progressText');
this.startProgressAnimation();
}
}
startProgressAnimation() {
this.progress = 0;
this.targetProgress = 0;
this.isAnimating = true;
// More stable progress simulation with smoother steps
const steps = [
{ progress: 8, delay: 150 },
{ progress: 20, delay: 250 },
{ progress: 35, delay: 400 },
{ progress: 50, delay: 350 },
{ progress: 65, delay: 500 },
{ progress: 78, delay: 450 },
{ progress: 88, delay: 600 },
{ progress: 95, delay: 700 },
];
let stepIndex = 0;
let isCancelled = false;
this.cancelProgress = () => { isCancelled = true; };
const updateProgress = () => {
if (isCancelled || !this.isAnimating) return;
if (stepIndex < steps.length) {
const step = steps[stepIndex];
this.targetProgress = step.progress;
stepIndex++;
setTimeout(updateProgress, step.delay);
} else {
// Keep at 95% until actual loading completes
this.targetProgress = 95;
}
};
updateProgress();
this.animateProgress();
}
animateProgress() {
if (!this.isAnimating || !this.progressFill) return;
// Smooth interpolation
const diff = this.targetProgress - this.progress;
if (Math.abs(diff) > 0.1) {
this.progress += diff * 0.15; // Smooth easing
} else {
this.progress = this.targetProgress;
}
if (this.progressFill) {
this.progressFill.style.width = `${this.progress}%`;
}
if (this.progressText) {
this.progressText.textContent = `${Math.round(this.progress)}%`;
}
// Add class when progress starts
if (this.progressBar) {
if (this.progress > 0) {
this.progressBar.classList.add('has-progress');
} else {
this.progressBar.classList.remove('has-progress');
}
}
if (this.isAnimating) {
this.animationFrame = requestAnimationFrame(() => this.animateProgress());
}
}
setProgress(value) {
this.targetProgress = Math.min(100, Math.max(0, value));
if (!this.isAnimating && this.progressFill) {
this.startProgressAnimation();
}
}
complete() {
this.targetProgress = 100;
// Animate to 100% and then hide
const checkComplete = () => {
if (this.progress >= 99.9) {
setTimeout(() => {
this.hide();
}, 200);
} else {
setTimeout(checkComplete, 50);
}
};
checkComplete();
}
hide() {
this.isAnimating = false;
if (this.cancelProgress) {
this.cancelProgress();
}
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
// Clear progress elements
this.progressBar = null;
this.progressFill = null;
this.progressText = null;
if (this.container) {
this.container.innerHTML = '';
}
}
}