feat: Enhance upload page tag suggestions with keyboard navigation and a top-aligned dropdown, while adding utility scripts for thumbnail copying and dummy data generation.

This commit is contained in:
x
2026-01-24 15:16:53 +01:00
parent 111f06ed42
commit c9ca037063
2 changed files with 50 additions and 47 deletions

View File

@@ -24,42 +24,7 @@
let tags = [];
let selectedFile = null;
// Flash Message Logic
const showFlash = (msg, type = 'success') => {
const existing = document.querySelector('.flash-message');
if (existing) existing.remove();
const flash = document.createElement('div');
flash.className = `flash-message ${type}`;
flash.textContent = msg;
Object.assign(flash.style, {
position: 'fixed',
top: '20px',
left: '50%',
transform: 'translateX(-50%)',
padding: '15px 30px',
borderRadius: '5px',
color: '#fff',
fontWeight: '600',
zIndex: '9999',
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
background: type === 'success' ? '#51cf66' : '#ff6b6b',
opacity: '0',
transition: 'opacity 0.3s'
});
document.body.appendChild(flash);
// Fade in
requestAnimationFrame(() => flash.style.opacity = '1');
// Remove after 5s
setTimeout(() => {
flash.style.opacity = '0';
setTimeout(() => flash.remove(), 300);
}, 5000);
};
const formatSize = (bytes) => {
const units = ['B', 'KB', 'MB', 'GB'];
@@ -207,10 +172,42 @@
updateSubmitButton();
};
let currentFocus = -1;
const addActive = (x) => {
if (!x) return false;
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
x[currentFocus].classList.add("active");
// Scroll to view
x[currentFocus].scrollIntoView({ block: 'nearest' });
};
const removeActive = (x) => {
for (let i = 0; i < x.length; i++) {
x[i].classList.remove("active");
}
};
tagInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const x = tagSuggestions.getElementsByClassName("tag-suggestion");
if (e.key === 'ArrowDown') {
currentFocus++;
addActive(x);
} else if (e.key === 'ArrowUp') {
currentFocus--;
addActive(x);
} else if (e.key === 'Enter') {
e.preventDefault();
addTag(tagInput.value);
if (currentFocus > -1) {
if (x) x[currentFocus].click();
} else {
addTag(tagInput.value);
}
} else if (e.key === 'Escape') {
tagSuggestions.classList.remove('show');
currentFocus = -1;
}
});
@@ -218,6 +215,7 @@
tagInput.addEventListener('input', () => {
clearTimeout(debounceTimer);
const query = tagInput.value.trim();
currentFocus = -1; // Reset focus on new input
if (query.length < 2) {
tagSuggestions.classList.remove('show');
@@ -239,7 +237,10 @@
tagSuggestions.classList.add('show');
tagSuggestions.querySelectorAll('.tag-suggestion').forEach(el => {
el.addEventListener('click', () => addTag(el.textContent));
el.addEventListener('click', () => {
addTag(el.textContent);
tagInput.focus();
});
});
} else {
tagSuggestions.classList.remove('show');
@@ -296,8 +297,7 @@
if (res.success) {
statusDiv.innerHTML = '✓ ' + res.msg;
statusDiv.className = 'upload-status success';
// Flash Message
showFlash(res.msg, 'success');
form.reset();
tags = [];
@@ -313,7 +313,7 @@
if (res.repost) {
statusDiv.innerHTML += ' <a href="/' + res.repost + '">View existing</a>';
}
showFlash('Upload failed: ' + res.msg, 'error');
}
submitBtn.querySelector('.btn-text').style.display = 'inline';
@@ -326,7 +326,7 @@
xhr.onerror = () => {
statusDiv.textContent = '✕ Upload failed. Please try again.';
statusDiv.className = 'upload-status error';
showFlash('Upload failed network error', 'error');
submitBtn.querySelector('.btn-text').style.display = 'inline';
submitBtn.querySelector('.btn-loading').style.display = 'none';
progressContainer.style.display = 'none';
@@ -340,7 +340,7 @@
console.error(err);
statusDiv.textContent = '✕ Upload failed: ' + err.message;
statusDiv.className = 'upload-status error';
showFlash('Upload failed: ' + err.message, 'error');
submitBtn.querySelector('.btn-text').style.display = 'inline';
submitBtn.querySelector('.btn-loading').style.display = 'none';
updateSubmitButton();