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

@@ -251,6 +251,7 @@
padding: 0.5rem;
display: flex;
flex-wrap: wrap;
position: relative;
gap: 0.5rem;
}
@@ -307,13 +308,14 @@
.tag-suggestions {
/* (styles for dropdown remain similar, maybe cleaner shadow) */
position: absolute;
top: 100%;
top: auto;
bottom: 100%;
left: 0;
right: 0;
background: #1e1e1e;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
border-radius: 0 0 4px 4px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.5);
border-radius: 4px 4px 0 0;
max-height: 200px;
overflow-y: auto;
display: none;
@@ -329,7 +331,8 @@
cursor: pointer;
}
.tag-suggestion:hover {
.tag-suggestion:hover,
.tag-suggestion.active {
background: rgba(255, 255, 255, 0.1);
}

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();