fix: checkbox in toolbar does not select all files (Fixes #25)

This commit is contained in:
Ryan
2025-04-27 15:34:41 -04:00
committed by GitHub
parent 41ade2e205
commit ae0d63b86f
3 changed files with 75 additions and 44 deletions

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## Changes 4/27/2025
- **Select-All** checkbox now correctly toggles all `.file-checkbox` inputs
- Updated `toggleAllCheckboxes(masterCheckbox)` to call `updateRowHighlight()` on each row so selections get the `.row-selected` highlight
- **Master checkbox sync** in toolbar
- Enhanced `updateFileActionButtons()` to set the header checkbox to checked, unchecked, or indeterminate based on how many files are selected
---
## Changes 4/26/2025 1.2.6 ## Changes 4/26/2025 1.2.6
**Apache / Dockerfile (CSP)** **Apache / Dockerfile (CSP)**
@@ -210,7 +219,7 @@
Refactored to: Refactored to:
1. Fetch CSRF 1. Fetch CSRF
2. POST credentials to `/api/auth/auth.php` 2. POST credentials to `/api/auth/auth.php`
3. On `totp_required`, refetch CSRF *again* before calling `openTOTPLoginModal()` 3. On `totp_required`, refetch CSRF again before calling `openTOTPLoginModal()`
4. Handle full logins vs. TOTP flows cleanly. 4. Handle full logins vs. TOTP flows cleanly.
- **TOTP handlers update** - **TOTP handlers update**
@@ -1156,7 +1165,7 @@ The enhancements extend the existing drag-and-drop functionality by adding a hea
- Adjusted file preview and icon styling for better alignment. - Adjusted file preview and icon styling for better alignment.
- Centered the header and optimized the layout for a clean, modern appearance. - Centered the header and optimized the layout for a clean, modern appearance.
*This changelog and feature summary reflect the improvements made during the refactor from a monolithic utils file to modular ES6 components, along with enhancements in UI responsiveness, sorting, file uploads, and file management operations.* This changelog and feature summary reflect the improvements made during the refactor from a monolithic utils file to modular ES6 components, along with enhancements in UI responsiveness, sorting, file uploads, and file management operations.
--- ---

View File

@@ -25,8 +25,9 @@ export function toggleAllCheckboxes(masterCheckbox) {
const checkboxes = document.querySelectorAll(".file-checkbox"); const checkboxes = document.querySelectorAll(".file-checkbox");
checkboxes.forEach(chk => { checkboxes.forEach(chk => {
chk.checked = masterCheckbox.checked; chk.checked = masterCheckbox.checked;
updateRowHighlight(chk);
}); });
updateFileActionButtons(); // update buttons based on current selection updateFileActionButtons();
} }
export function updateFileActionButtons() { export function updateFileActionButtons() {
@@ -38,6 +39,21 @@ export function updateFileActionButtons() {
const zipBtn = document.getElementById("downloadZipBtn"); const zipBtn = document.getElementById("downloadZipBtn");
const extractZipBtn = document.getElementById("extractZipBtn"); const extractZipBtn = document.getElementById("extractZipBtn");
// keep the “select all” in sync ——
const master = document.getElementById("selectAll");
if (master) {
if (selectedCheckboxes.length === fileCheckboxes.length) {
master.checked = true;
master.indeterminate = false;
} else if (selectedCheckboxes.length === 0) {
master.checked = false;
master.indeterminate = false;
} else {
master.checked = false;
master.indeterminate = true;
}
}
if (fileCheckboxes.length === 0) { if (fileCheckboxes.length === 0) {
if (copyBtn) copyBtn.style.display = "none"; if (copyBtn) copyBtn.style.display = "none";
if (moveBtn) moveBtn.style.display = "none"; if (moveBtn) moveBtn.style.display = "none";
@@ -271,8 +287,6 @@ export function toggleRowSelection(event, fileName) {
const start = Math.min(currentIndex, lastIndex); const start = Math.min(currentIndex, lastIndex);
const end = Math.max(currentIndex, lastIndex); const end = Math.max(currentIndex, lastIndex);
// If neither CTRL nor Meta is pressed, you might choose
// to clear existing selections. For this example we leave existing selections intact.
for (let i = start; i <= end; i++) { for (let i = start; i <= end; i++) {
const cb = allRows[i].querySelector(".file-checkbox"); const cb = allRows[i].querySelector(".file-checkbox");
if (cb) { if (cb) {

View File

@@ -340,47 +340,55 @@ export function renderFileTable(folder, container) {
fileListContent.innerHTML = combinedTopHTML + headerHTML + rowsHTML + bottomControlsHTML; fileListContent.innerHTML = combinedTopHTML + headerHTML + rowsHTML + bottomControlsHTML;
// hook up the master checkbox
const selectAll = document.getElementById("selectAll");
if (selectAll) {
selectAll.addEventListener("change", () => {
toggleAllCheckboxes(selectAll);
});
}
// 1) Row-click selects the row // 1) Row-click selects the row
fileListContent.querySelectorAll("tbody tr").forEach(row => { fileListContent.querySelectorAll("tbody tr").forEach(row => {
row.addEventListener("click", e => { row.addEventListener("click", e => {
// grab the underlying checkbox value // grab the underlying checkbox value
const cb = row.querySelector(".file-checkbox"); const cb = row.querySelector(".file-checkbox");
if (!cb) return; if (!cb) return;
toggleRowSelection(e, cb.value); toggleRowSelection(e, cb.value);
});
}); });
});
// 2) Download buttons
// 2) Download buttons fileListContent.querySelectorAll(".download-btn").forEach(btn => {
fileListContent.querySelectorAll(".download-btn").forEach(btn => { btn.addEventListener("click", e => {
btn.addEventListener("click", e => { e.stopPropagation();
e.stopPropagation(); openDownloadModal(btn.dataset.downloadName, btn.dataset.downloadFolder);
openDownloadModal(btn.dataset.downloadName, btn.dataset.downloadFolder); });
}); });
});
// 3) Edit buttons
// 3) Edit buttons fileListContent.querySelectorAll(".edit-btn").forEach(btn => {
fileListContent.querySelectorAll(".edit-btn").forEach(btn => { btn.addEventListener("click", e => {
btn.addEventListener("click", e => { e.stopPropagation();
e.stopPropagation(); editFile(btn.dataset.editName, btn.dataset.editFolder);
editFile(btn.dataset.editName, btn.dataset.editFolder); });
}); });
});
// 4) Rename buttons
// 4) Rename buttons fileListContent.querySelectorAll(".rename-btn").forEach(btn => {
fileListContent.querySelectorAll(".rename-btn").forEach(btn => { btn.addEventListener("click", e => {
btn.addEventListener("click", e => { e.stopPropagation();
e.stopPropagation(); renameFile(btn.dataset.renameName, btn.dataset.renameFolder);
renameFile(btn.dataset.renameName, btn.dataset.renameFolder); });
}); });
});
// 5) Preview buttons (if you still have a .preview-btn)
// 5) Preview buttons (if you still have a .preview-btn) fileListContent.querySelectorAll(".preview-btn").forEach(btn => {
fileListContent.querySelectorAll(".preview-btn").forEach(btn => { btn.addEventListener("click", e => {
btn.addEventListener("click", e => { e.stopPropagation();
e.stopPropagation(); previewFile(btn.dataset.previewUrl, btn.dataset.previewName);
previewFile(btn.dataset.previewUrl, btn.dataset.previewName); });
}); });
});
createViewToggleButton(); createViewToggleButton();
@@ -575,7 +583,7 @@ export function renderGalleryView(folder, container) {
style="position:absolute; top:5px; left:5px; width:16px; height:16px;"></label> style="position:absolute; top:5px; left:5px; width:16px; height:16px;"></label>
<div class="gallery-preview" style="cursor:pointer;" <div class="gallery-preview" style="cursor:pointer;"
data-preview-url="${folderPath+encodeURIComponent(file.name)}?t=${Date.now()}" data-preview-url="${folderPath + encodeURIComponent(file.name)}?t=${Date.now()}"
data-preview-name="${file.name}"> data-preview-name="${file.name}">
${thumbnail} ${thumbnail}
</div> </div>
@@ -590,20 +598,20 @@ export function renderGalleryView(folder, container) {
<div class="button-wrap" style="display:flex; justify-content:center; gap:5px; margin-top:5px;"> <div class="button-wrap" style="display:flex; justify-content:center; gap:5px; margin-top:5px;">
<button type="button" class="btn btn-sm btn-success download-btn" <button type="button" class="btn btn-sm btn-success download-btn"
data-download-name="${escapeHTML(file.name)}" data-download-name="${escapeHTML(file.name)}"
data-download-folder="${file.folder||"root"}" data-download-folder="${file.folder || "root"}"
title="${t('download')}"> title="${t('download')}">
<i class="material-icons">file_download</i> <i class="material-icons">file_download</i>
</button> </button>
${file.editable ? ` ${file.editable ? `
<button type="button" class="btn btn-sm edit-btn" <button type="button" class="btn btn-sm edit-btn"
data-edit-name="${escapeHTML(file.name)}" data-edit-name="${escapeHTML(file.name)}"
data-edit-folder="${file.folder||"root"}" data-edit-folder="${file.folder || "root"}"
title="${t('edit')}"> title="${t('edit')}">
<i class="material-icons">edit</i> <i class="material-icons">edit</i>
</button>` : ""} </button>` : ""}
<button type="button" class="btn btn-sm btn-warning rename-btn" <button type="button" class="btn btn-sm btn-warning rename-btn"
data-rename-name="${escapeHTML(file.name)}" data-rename-name="${escapeHTML(file.name)}"
data-rename-folder="${file.folder||"root"}" data-rename-folder="${file.folder || "root"}"
title="${t('rename')}"> title="${t('rename')}">
<i class="material-icons">drive_file_rename_outline</i> <i class="material-icons">drive_file_rename_outline</i>
</button> </button>