parcel/templates/uploads/new.html

148 lines
4.2 KiB
HTML

<div class="panel thin gap-2">
<h1 class="heading">New Upload</h1>
<form id="upload-form" class="form">
<input type="file" class="field" name="file" multiple>
<div class="progress-container flex flex-row gap-2 hidden">
<progress id="progress" value="0" max="100" class="grow"></progress>
<div class="text-right" style="min-width: 8rem"></div>
</div>
<div class="buttons end mt-4">
<button type="submit" class="button" disabled>
{% include "icons/lucide/upload.svg" %}
Upload file
</button>
</div>
</form>
</div>
<script>
const form = document.querySelector("#upload-form");
const input = form.querySelector("input[type='file']");
const submit = form.querySelector("button[type='submit']");
const progressContainer = form.querySelector(".progress-container");
const progressElement = progressContainer.querySelector("progress");
const progressText = progressContainer.querySelector("div");
function getErrorElement() {
const existing = form.querySelector("#error");
if (existing) {
return existing;
}
const element = document.createElement("DIV");
element.classNames.add("text-danger");
form.appendChild(element);
return element;
}
function setError(message) {
progressText.innerHtml = "0%";
progressElement.value = 0;
progressContainer.classList.add("hidden");
getErrorElement().innerHTML = message;
}
input.addEventListener("change", (event) => {
console.log("Number of selected files changed to", event.target.files.length);
submit.disabled = event.target.files.length === 0;
});
let formatter = null, factor = 1;
function setupFormatter(total_size) {
let unit, digits = 0;
if (total_size > 1024 * 1024 * 1024) {
unit = "gigabyte";
factor = 1024 * 1024 * 1024;
digits = 2;
} else if (total_size > 1024 * 1024) {
unit = "megabyte";
factor = 1024 * 1024;
} else if (total_size > 1024) {
unit = "kilobyte";
factor = 1024;
} else {
unit = "byte";
factor = 1;
}
console.log("Using formatter with unit", unit, "and factor", factor, "and digits", digits);
formatter = new Intl.NumberFormat(undefined, {
style: "unit",
unit: unit,
unitDisplay: "short",
minimumFractionDigits: digits,
maximumFractionDigits: digits,
});
}
form.addEventListener("submit", (event) => {
event.preventDefault();
submit.disabled = true;
progressElement.value = 0;
progressText.innerHTML = "";
var total_files = 0, total_size = 0;
const form_data = new FormData();
for (let field of event.target.elements) {
if (field.tagName !== "INPUT") {
continue;
}
if (field.type === "file") {
for (let file of field.files) {
total_files += 1;
total_size += file.size;
form_data.append("file", file);
}
}
}
if (total_files === 0) {
console.warn("No files selected, not submitting form");
return;
}
const request = new XMLHttpRequest();
request.addEventListener("load", (event) => {
console.log("Upload finished");
htmx.trigger("#upload-refresh", "refresh-event", {});
input.value = null;
progressElement.value = 0;
progressText.innerHTML = "";
progressContainer.classList.add("hidden");
submit.disabled = true;
});
request.addEventListener("error", (event) => {
console.error("Failed to upload file", event);
setError("Encountered an error uploading the file");
});
request.addEventListener("abort", (event) => {
console.warn("Upload was aborted", event);
setError("Upload was aborted");
});
request.upload.addEventListener("progress", (event) => {
const amount = Math.round(event.loaded / total_size * 100.0);
progressElement.value = amount;
progressText.innerHTML =
formatter.format(event.loaded / factor) + " / " +
formatter.format(total_size / factor);
})
setupFormatter(total_size);
progressContainer.classList.remove("hidden");
console.log("Starting upload of", total_files, "files with a total size of", total_size, "bytes");
request.open("POST", "/uploads");
request.send(form_data);
});
</script>