search and drop fixes

This commit is contained in:
Ryan
2025-03-15 02:28:57 -04:00
committed by GitHub
parent 086dfa402b
commit 332d620921
2 changed files with 95 additions and 73 deletions

View File

@@ -123,22 +123,29 @@ export function loadFileList(folderParam) {
}); });
} }
// Debounce helper (if not defined already)
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
export function renderFileTable(folder) { export function renderFileTable(folder) {
const fileListContainer = document.getElementById("fileList"); const fileListContainer = document.getElementById("fileList");
const folderPath = (folder === "root") const folderPath = (folder === "root")
? "uploads/" ? "uploads/"
: "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/"; : "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/";
// Attempt to get the search input element. // Use the global search term if available.
const searchInputElement = document.getElementById("searchInput"); const searchTerm = window.currentSearchTerm || "";
const searchHadFocus = searchInputElement && (document.activeElement === searchInputElement);
const searchTerm = searchInputElement ? searchInputElement.value : "";
const filteredFiles = fileData.filter(file => const filteredFiles = fileData.filter(file =>
file.name.toLowerCase().includes(searchTerm.toLowerCase()) file.name.toLowerCase().includes(searchTerm.toLowerCase())
); );
// Read persistent items per page from localStorage, default to 10. // Get persistent items per page from localStorage.
const itemsPerPageSetting = parseInt(localStorage.getItem('itemsPerPage') || '10', 10); const itemsPerPageSetting = parseInt(localStorage.getItem('itemsPerPage') || '10', 10);
const currentPage = window.currentPage || 1; const currentPage = window.currentPage || 1;
const totalFiles = filteredFiles.length; const totalFiles = filteredFiles.length;
@@ -146,25 +153,25 @@ export function renderFileTable(folder) {
const safeSearchTerm = escapeHTML(searchTerm); const safeSearchTerm = escapeHTML(searchTerm);
const topControlsHTML = ` const topControlsHTML = `
<div class="row align-items-center mb-3"> <div class="row align-items-center mb-3">
<div class="col-12 col-md-8 mb-2 mb-md-0"> <div class="col-12 col-md-8 mb-2 mb-md-0">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text" id="searchIcon"> <span class="input-group-text" id="searchIcon">
<i class="material-icons">search</i> <i class="material-icons">search</i>
</span> </span>
</div>
<input type="text" id="searchInput" class="form-control" placeholder="Search files..." value="${safeSearchTerm}" aria-describedby="searchIcon">
</div>
</div>
<div class="col-12 col-md-4 text-left">
<div class="d-flex justify-content-center justify-content-md-start align-items-center">
<button class="custom-prev-next-btn" ${currentPage === 1 ? "disabled" : ""} onclick="changePage(${currentPage - 1})">Prev</button>
<span class="page-indicator">Page ${currentPage} of ${totalPages || 1}</span>
<button class="custom-prev-next-btn" ${currentPage === totalPages || totalFiles === 0 ? "disabled" : ""} onclick="changePage(${currentPage + 1})">Next</button>
</div> </div>
<input type="text" id="searchInput" class="form-control" placeholder="Search files..." value="${safeSearchTerm}" aria-describedby="searchIcon">
</div> </div>
</div> </div>
<div class="col-12 col-md-4 text-left">
<div class="d-flex justify-content-center justify-content-md-start align-items-center">
<button class="custom-prev-next-btn" ${currentPage === 1 ? "disabled" : ""} onclick="changePage(${currentPage - 1})">Prev</button>
<span class="page-indicator">Page ${currentPage} of ${totalPages || 1}</span>
<button class="custom-prev-next-btn" ${currentPage === totalPages || totalFiles === 0 ? "disabled" : ""} onclick="changePage(${currentPage + 1})">Next</button>
</div>
</div>
</div>
`; `;
let tableHTML = ` let tableHTML = `
@@ -195,13 +202,12 @@ export function renderFileTable(folder) {
const safeSize = escapeHTML(file.size); const safeSize = escapeHTML(file.size);
const safeUploader = escapeHTML(file.uploader || "Unknown"); const safeUploader = escapeHTML(file.uploader || "Unknown");
// Check if file is an image.
const isImage = /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name); const isImage = /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name);
const previewButton = isImage const previewButton = isImage
? `<button class="btn btn-sm btn-info ml-2" onclick="event.stopPropagation(); previewImage('${folderPath + encodeURIComponent(file.name)}', '${safeFileName}')"> ? `<button class="btn btn-sm btn-info ml-2" onclick="event.stopPropagation(); previewImage('${folderPath + encodeURIComponent(file.name)}', '${safeFileName}')">
<i class="material-icons">image</i> <i class="material-icons">image</i>
</button>` </button>`
: ""; : "";
tableBody += ` tableBody += `
@@ -235,8 +241,8 @@ export function renderFileTable(folder) {
<label class="label-inline mr-2 mb-0">Show</label> <label class="label-inline mr-2 mb-0">Show</label>
<select class="form-control bottom-select" onchange="changeItemsPerPage(this.value)"> <select class="form-control bottom-select" onchange="changeItemsPerPage(this.value)">
${[10, 20, 50, 100] ${[10, 20, 50, 100]
.map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`) .map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`)
.join("")} .join("")}
</select> </select>
<span class="items-per-page-text ml-2 mb-0">items per page</span> <span class="items-per-page-text ml-2 mb-0">items per page</span>
</div> </div>
@@ -244,14 +250,25 @@ export function renderFileTable(folder) {
fileListContainer.innerHTML = topControlsHTML + tableHTML + tableBody + bottomControlsHTML; fileListContainer.innerHTML = topControlsHTML + tableHTML + tableBody + bottomControlsHTML;
// Only add event listeners if searchInputElement exists. // Re-attach event listener for the new search input element.
if (searchInputElement) { const newSearchInput = document.getElementById("searchInput");
searchInputElement.addEventListener("input", function () { if (newSearchInput) {
newSearchInput.addEventListener("input", debounce(function () {
window.currentSearchTerm = newSearchInput.value;
window.currentPage = 1; window.currentPage = 1;
renderFileTable(folder); renderFileTable(folder);
}); // After re-rendering, restore focus and caret position.
setTimeout(() => {
const freshInput = document.getElementById("searchInput");
if (freshInput) {
freshInput.focus();
freshInput.setSelectionRange(freshInput.value.length, freshInput.value.length);
}
}, 0);
}, 300));
} }
// Add event listeners for header sorting.
document.querySelectorAll("table.table thead th[data-column]").forEach(cell => { document.querySelectorAll("table.table thead th[data-column]").forEach(cell => {
cell.addEventListener("click", function () { cell.addEventListener("click", function () {
const column = this.getAttribute("data-column"); const column = this.getAttribute("data-column");
@@ -259,6 +276,7 @@ export function renderFileTable(folder) {
}); });
}); });
// Add event listeners for checkboxes.
document.querySelectorAll('#fileList .file-checkbox').forEach(checkbox => { document.querySelectorAll('#fileList .file-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', function (e) { checkbox.addEventListener('change', function (e) {
updateRowHighlight(e.target); updateRowHighlight(e.target);

View File

@@ -45,6 +45,29 @@ function getFilesFromDataTransferItems(items) {
return Promise.all(promises).then(results => results.flat()); return Promise.all(promises).then(results => results.flat());
} }
// Helper: Set default drop area content.
// Moved to module scope so it is available globally in this module.
function setDropAreaDefault() {
const dropArea = document.getElementById("uploadDropArea");
if (dropArea) {
dropArea.innerHTML = `
<div id="uploadInstruction" class="upload-instruction">
Drop files/folders here or click 'Choose files'
</div>
<div id="uploadFileRow" class="upload-file-row">
<button id="customChooseBtn" type="button">
Choose files
</button>
</div>
<div id="fileInfoWrapper" class="file-info-wrapper">
<div id="fileInfoContainer" class="file-info-container">
<span id="fileInfoDefault">No files selected</span>
</div>
</div>
`;
}
}
function adjustFolderHelpExpansion() { function adjustFolderHelpExpansion() {
const uploadCard = document.getElementById("uploadCard"); const uploadCard = document.getElementById("uploadCard");
const folderHelpDetails = document.querySelector(".folder-help-details"); const folderHelpDetails = document.querySelector(".folder-help-details");
@@ -247,8 +270,8 @@ function processFiles(filesInput) {
} }
const listWrapper = document.createElement("div"); const listWrapper = document.createElement("div");
listWrapper.classList.add("upload-progress-wrapper"); listWrapper.classList.add("upload-progress-wrapper");
// Set a maximum height (adjust as needed) and enable vertical scrolling. // Set a maximum height and enable vertical scrolling.
listWrapper.style.maxHeight = "300px"; // for example, 300px listWrapper.style.maxHeight = "300px";
listWrapper.style.overflowY = "auto"; listWrapper.style.overflowY = "auto";
listWrapper.appendChild(list); listWrapper.appendChild(list);
progressContainer.appendChild(listWrapper); progressContainer.appendChild(listWrapper);
@@ -433,30 +456,11 @@ function initUpload() {
fileInput.setAttribute("directory", ""); fileInput.setAttribute("directory", "");
} }
// Helper: Set default drop area content. // Set default drop area content.
function setDropAreaDefault() { setDropAreaDefault();
if (dropArea) {
dropArea.innerHTML = `
<div id="uploadInstruction" class="upload-instruction">
Drop files/folders here or click 'Choose files'
</div>
<div id="uploadFileRow" class="upload-file-row">
<button id="customChooseBtn" type="button">
Choose files
</button>
</div>
<div id="fileInfoWrapper" class="file-info-wrapper">
<div id="fileInfoContainer" class="file-info-container">
<span id="fileInfoDefault">No files selected</span>
</div>
</div>
`;
}
}
if (dropArea) { if (dropArea) {
dropArea.classList.add("upload-drop-area"); dropArea.classList.add("upload-drop-area");
setDropAreaDefault();
dropArea.addEventListener("dragover", function (e) { dropArea.addEventListener("dragover", function (e) {
e.preventDefault(); e.preventDefault();
// Use a darker color if dark mode is active. // Use a darker color if dark mode is active.