1 Commits
v1.0.2 ... main

Author SHA1 Message Date
Toni
fa0c998d0f upload progress bar 2026-02-09 16:27:58 +01:00
3 changed files with 110 additions and 7 deletions

View File

@@ -385,22 +385,87 @@ dropZone.addEventListener('drop', (e) => {
} }
}); });
// File Input Change
document.getElementById('file-input').addEventListener('change', (e) => {
if (e.target.files.length > 0) {
uploadFiles(e.target.files);
// Reset input so same file can be selected again if needed
e.target.value = '';
}
});
async function uploadFiles(files) { async function uploadFiles(files) {
const statusDiv = document.getElementById('upload-status'); const statusDiv = document.getElementById('upload-status');
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
statusDiv.innerHTML = 'Uploading...'; statusDiv.innerHTML = 'Uploading...';
progressContainer.classList.remove('hidden');
progressBar.style.width = '0%';
progressText.textContent = '0%';
let totalSize = 0;
for (let file of files) totalSize += file.size;
// Avoid division by zero
if (totalSize === 0) totalSize = 1;
let previousFilesSize = 0;
for (let file of files) { for (let file of files) {
try {
await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
if (currentPath) {
await fetch(`/api/upload?path=${encodeURIComponent(currentPath)}`, { method: 'POST', body: formData }); const url = currentPath
? `/api/upload?path=${encodeURIComponent(currentPath)}`
: '/api/upload';
xhr.open('POST', url);
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const currentLoaded = e.loaded;
const totalLoaded = previousFilesSize + currentLoaded;
const percent = Math.min(100, Math.round((totalLoaded / totalSize) * 100));
progressBar.style.width = percent + '%';
progressText.textContent = percent + '%';
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve();
} else { } else {
await fetch('/api/upload', { method: 'POST', body: formData }); reject(new Error(`Upload failed: ${xhr.statusText}`));
}
};
xhr.onerror = () => reject(new Error('Network Error'));
xhr.send(formData);
});
previousFilesSize += file.size;
} catch (e) {
console.error(e);
statusDiv.innerHTML = `<span style="color:var(--danger-color)">Error uploading ${file.name}</span>`;
} }
} }
progressBar.style.width = '100%';
progressText.textContent = '100%';
statusDiv.innerHTML = 'Done!'; statusDiv.innerHTML = 'Done!';
setTimeout(() => statusDiv.innerHTML = '', 2000);
setTimeout(() => {
statusDiv.innerHTML = '';
progressContainer.classList.add('hidden');
progressBar.style.width = '0%';
progressText.textContent = '0%';
}, 2000);
loadFiles(); loadFiles();
} }

View File

@@ -67,6 +67,10 @@
Files</button> Files</button>
</div> </div>
<div id="upload-status"></div> <div id="upload-status"></div>
<div id="progress-container" class="hidden">
<div id="progress-bar"></div>
<div id="progress-text">0%</div>
</div>
<!-- File List --> <!-- File List -->
<section class="files-section"> <section class="files-section">

View File

@@ -246,6 +246,40 @@ header {
transform: scale(1.02); transform: scale(1.02);
} }
/* Progress Bar */
#progress-container {
width: 100%;
background-color: var(--card-bg);
border-radius: 0.5rem;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
position: relative;
height: 1.5rem;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
#progress-bar {
position: absolute;
left: 0;
top: 0;
height: 100%;
background-color: var(--primary-color);
width: 0%;
transition: width 0.2s;
}
#progress-text {
position: relative;
z-index: 1;
font-size: 0.8rem;
font-weight: 600;
color: white;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
/* Modals */ /* Modals */
.modal { .modal {
position: fixed; position: fixed;