From 49629343d58ec2ec6b1372e3c8cff7d781b840c2 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Thu, 1 Jan 2026 17:08:01 +0100 Subject: [PATCH] Auto-commit local changes before build (2026-01-01 17:08:01) --- .last-branch | 2 +- .../src/templates/main/run_checks.html | 98 ++++++++++++------- docs/changelog.md | 9 ++ temp.md | 1 - 4 files changed, 75 insertions(+), 35 deletions(-) delete mode 100644 temp.md diff --git a/.last-branch b/.last-branch index b2a72a3..093e0ca 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260101-06-run-checks-shift-multiselect-fix +v20260101-07-run-checks-shift-multiselect-delegation-fix diff --git a/containers/backupchecks/src/templates/main/run_checks.html b/containers/backupchecks/src/templates/main/run_checks.html index d5c8bd0..8eb2235 100644 --- a/containers/backupchecks/src/templates/main/run_checks.html +++ b/containers/backupchecks/src/templates/main/run_checks.html @@ -286,6 +286,9 @@ var btnMarkAllReviewed = document.getElementById('rcm_mark_all_reviewed'); + // Shift-click range selection for checkbox rows + var lastCheckedCb = null; + function statusClass(status) { var s = (status || "").toString().toLowerCase(); @@ -348,9 +351,18 @@ if (btnMark) btnMark.disabled = ids.length === 0; if (btnUnmark) btnUnmark.disabled = ids.length === 0; if (statusEl) statusEl.textContent = ids.length ? (ids.length + ' selected') : ''; + refreshSelectAll(); } - var lastRowCheckbox = null; + function refreshSelectAll() { + if (!selectAll) return; + var cbs = table.querySelectorAll('tbody .rc_row_cb'); + var total = cbs.length; + var checked = 0; + cbs.forEach(function (cb) { if (cb.checked) checked++; }); + selectAll.indeterminate = checked > 0 && checked < total; + selectAll.checked = total > 0 && checked === total; + } if (selectAll) { selectAll.addEventListener('change', function () { @@ -360,47 +372,67 @@ }); } - // Shift-click multi select for row checkboxes (continuous range) - table.addEventListener('click', function (e) { - if (!(e.target && e.target.classList && e.target.classList.contains('rc_row_cb'))) return; +// Prevent browser text selection when using Shift with checkbox selection +table.addEventListener('mousedown', function (e) { + var t = e.target; + if (!t) return; + if (t.classList && t.classList.contains('rc_row_cb') && e.shiftKey) { + e.preventDefault(); + if (window.getSelection) { + try { window.getSelection().removeAllRanges(); } catch (err) {} + } + } +}, true); - // Prevent row click handlers and stop the browser from doing text-range selection. - e.stopPropagation(); +// Handle Shift-click range selection on the checkbox inputs (delegated) +table.addEventListener('click', function (e) { + var t = e.target; + if (!t || !(t.classList && t.classList.contains('rc_row_cb'))) return; - // If shift is held, apply the checkbox state to the whole range. - if (e.shiftKey && lastRowCheckbox && lastRowCheckbox !== e.target) { - var cbs = Array.prototype.slice.call(table.querySelectorAll('tbody .rc_row_cb')); - var start = cbs.indexOf(lastRowCheckbox); - var end = cbs.indexOf(e.target); + // Custom handling so we can reliably read shiftKey and control the toggle + range + if (e.shiftKey && lastCheckedCb && lastCheckedCb !== t) { + e.preventDefault(); + e.stopPropagation(); - if (start !== -1 && end !== -1) { - var min = Math.min(start, end); - var max = Math.max(start, end); - var state = e.target.checked; + // The default action would toggle the checkbox; do it manually + var targetChecked = !t.checked; + t.checked = targetChecked; - for (var i = min; i <= max; i++) { - cbs[i].checked = state; - } - } + var cbs = Array.prototype.slice.call(table.querySelectorAll('tbody .rc_row_cb')); + var start = cbs.indexOf(lastCheckedCb); + var end = cbs.indexOf(t); + + if (start !== -1 && end !== -1) { + var lo = Math.min(start, end); + var hi = Math.max(start, end); + for (var i = lo; i <= hi; i++) { + cbs[i].checked = targetChecked; } + } - lastRowCheckbox = e.target; + if (window.getSelection) { + try { window.getSelection().removeAllRanges(); } catch (err) {} + } - // Clear any accidental text selection caused by Shift-click. - try { - var sel = window.getSelection ? window.getSelection() : null; - if (sel && sel.removeAllRanges) sel.removeAllRanges(); - } catch (err) {} + updateButtons(); + lastCheckedCb = t; + return; + } - updateButtons(); - }); + // Normal click: let default toggle happen, but remember the last checkbox + lastCheckedCb = t; - // Fallback: any checkbox change should still update the buttons. - table.addEventListener('change', function (e) { - if (e.target && e.target.classList && e.target.classList.contains('rc_row_cb')) { - updateButtons(); - } - }); + // Defer update until after the checkbox state changes + setTimeout(updateButtons, 0); +}, true); + +// Fallback for keyboard toggles (space) and other state changes +table.addEventListener('change', function (e) { + if (e.target && e.target.classList && e.target.classList.contains('rc_row_cb')) { + lastCheckedCb = e.target; + updateButtons(); + } +}); function postJson(url, body) { return fetch(url, { diff --git a/docs/changelog.md b/docs/changelog.md index b989510..9c77e53 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -142,6 +142,15 @@ - Implemented tracking of the last selected checkbox to enable continuous range selection. - Ensured multiselect only applies to checkbox interactions and not table row text. +--- + +## v20260101-07-run-checks-shift-multiselect-delegation-fix + +- Reworked Shift-click multiselect on the Run Checks page using event delegation. +- Ensured checkbox range selection works correctly with dynamically rendered table rows. +- Disabled default browser text selection when using Shift during checkbox interaction. +- Applied Gmail-style range selection logic for consistent multi-row selection behavior. + ================================================================================================================================================ ## v0.1.14 diff --git a/temp.md b/temp.md deleted file mode 100644 index 6a749b5..0000000 --- a/temp.md +++ /dev/null @@ -1 +0,0 @@ -left empty \ No newline at end of file -- 2.45.2