114 lines
3.8 KiB
HTML
114 lines
3.8 KiB
HTML
{% extends 'base.html' %}
|
||
{% block content %}
|
||
<section class="card upload-card">
|
||
<h1>{{ t('upload') }}</h1>
|
||
<p class="upload-intro">{{ t('upload_intro') }}</p>
|
||
<form id="upload-form" method="post" enctype="multipart/form-data" class="form-grid">
|
||
<label class="upload-picker" for="photo-input">
|
||
<span class="upload-picker-title">{{ t('file') }}</span>
|
||
<span class="upload-picker-subtitle">{{ t('upload_picker_hint') }}</span>
|
||
<input id="photo-input" class="sr-only" type="file" name="photo" accept="image/jpeg,image/png,image/jpg,image/heic,image/heif,.heic,.heif" multiple />
|
||
</label>
|
||
<p id="upload-selected-count" class="upload-count"></p>
|
||
<p id="upload-ready-hint" class="upload-ready">{{ t('upload_ready') }}</p>
|
||
<ul id="upload-file-list" class="upload-file-list"></ul>
|
||
<button id="upload-submit-btn" class="btn" type="submit" disabled>{{ t('upload_submit') }}</button>
|
||
</form>
|
||
</section>
|
||
|
||
<script>
|
||
(() => {
|
||
const form = document.getElementById("upload-form");
|
||
const fileInput = document.getElementById("photo-input");
|
||
const countEl = document.getElementById("upload-selected-count");
|
||
const readyEl = document.getElementById("upload-ready-hint");
|
||
const listEl = document.getElementById("upload-file-list");
|
||
const submitBtn = document.getElementById("upload-submit-btn");
|
||
const countTpl = {{ t('upload_selected_count')|tojson }};
|
||
const selectedFiles = [];
|
||
|
||
const fileKey = (file) => `${file.name}__${file.size}__${file.lastModified}`;
|
||
|
||
const syncInputFiles = () => {
|
||
const transfer = new DataTransfer();
|
||
selectedFiles.forEach((file) => transfer.items.add(file));
|
||
fileInput.files = transfer.files;
|
||
};
|
||
|
||
const renderSelection = () => {
|
||
if (!selectedFiles.length) {
|
||
countEl.textContent = "";
|
||
listEl.innerHTML = "";
|
||
if (readyEl) {
|
||
readyEl.classList.remove("is-visible");
|
||
}
|
||
if (submitBtn) {
|
||
submitBtn.disabled = true;
|
||
}
|
||
return;
|
||
}
|
||
|
||
countEl.textContent = countTpl.replace("{count}", String(selectedFiles.length));
|
||
listEl.innerHTML = "";
|
||
if (readyEl) {
|
||
readyEl.classList.add("is-visible");
|
||
}
|
||
if (submitBtn) {
|
||
submitBtn.disabled = false;
|
||
}
|
||
|
||
selectedFiles.slice(0, 20).forEach((file, index) => {
|
||
const item = document.createElement("li");
|
||
const label = document.createElement("span");
|
||
label.textContent = file.name;
|
||
|
||
const removeBtn = document.createElement("button");
|
||
removeBtn.type = "button";
|
||
removeBtn.className = "upload-file-remove";
|
||
removeBtn.setAttribute("aria-label", "remove file");
|
||
removeBtn.textContent = "×";
|
||
removeBtn.addEventListener("click", () => {
|
||
selectedFiles.splice(index, 1);
|
||
syncInputFiles();
|
||
renderSelection();
|
||
});
|
||
|
||
item.appendChild(label);
|
||
item.appendChild(removeBtn);
|
||
listEl.appendChild(item);
|
||
});
|
||
if (selectedFiles.length > 20) {
|
||
const more = document.createElement("li");
|
||
more.textContent = `+ ${selectedFiles.length - 20} weitere`;
|
||
listEl.appendChild(more);
|
||
}
|
||
};
|
||
|
||
fileInput.addEventListener("change", () => {
|
||
const existingKeys = new Set(selectedFiles.map(fileKey));
|
||
Array.from(fileInput.files || []).forEach((file) => {
|
||
const key = fileKey(file);
|
||
if (!existingKeys.has(key)) {
|
||
selectedFiles.push(file);
|
||
existingKeys.add(key);
|
||
}
|
||
});
|
||
syncInputFiles();
|
||
fileInput.value = "";
|
||
renderSelection();
|
||
});
|
||
|
||
form.addEventListener("submit", (event) => {
|
||
if (!selectedFiles.length) {
|
||
event.preventDefault();
|
||
renderSelection();
|
||
return;
|
||
}
|
||
syncInputFiles();
|
||
});
|
||
|
||
renderSelection();
|
||
})();
|
||
</script>
|
||
{% endblock %}
|