initial
This commit is contained in:
158
static/components/LoadingProgress.js
Normal file
158
static/components/LoadingProgress.js
Normal file
@@ -0,0 +1,158 @@
|
||||
// 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 = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user