From d4f0c053412deba54710ce5846edd3388fee529d Mon Sep 17 00:00:00 2001 From: Ross Date: Wed, 25 Mar 2026 22:07:56 +0000 Subject: [PATCH] Enhance row selection functionality in SelectionTable to support shift-range selection and improve checkbox handling --- generic/tables.py | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/generic/tables.py b/generic/tables.py index 7b7f383c..286f50d5 100644 --- a/generic/tables.py +++ b/generic/tables.py @@ -59,6 +59,7 @@ class SelectionTable(tables.Table): self.caption = self.row_selection_controls + @property def row_selection_controls(self): token = (self.attrs or {}).get('data-selection-token', format(id(self), 'x')) @@ -107,9 +108,9 @@ class SelectionTable(tables.Table): setCheckboxesDisabled(true); hideSelectionColumn(); table.addEventListener('click', function(e){ - if (!selectionEnabled) return; - var tag = (e.target.tagName || '').toLowerCase(); - if (['a','button','select','textarea','label'].indexOf(tag) !== -1) return; + // Checkbox clicks should always be handled (support shift-range selection), + // even when row-selection mode is not enabled. Row clicks to toggle a + // checkbox still require selectionEnabled. if (e.target && e.target.matches && e.target.matches('input[type="checkbox"]')){ var cb = e.target; if (e.shiftKey && lastChecked && lastChecked !== cb){ @@ -125,9 +126,37 @@ class SelectionTable(tables.Table): lastChecked = cb; return; } + + if (!selectionEnabled) return; + var tag = (e.target.tagName || '').toLowerCase(); + if (['a','button','select','textarea','label'].indexOf(tag) !== -1) return; + var tr = e.target.closest('tr'); if (!tr || !table.contains(tr)) return; var cb = tr.querySelector('input[type="checkbox"][name="selection"]') || tr.querySelector('input[type="checkbox"]'); - if (cb && !cb.disabled){ cb.checked = !cb.checked; tr.classList.toggle('table-active', cb.checked); updateSelectedCount(); lastChecked = cb; } + if (!cb || cb.disabled) return; + + // If the user shift-clicks on a row, perform range selection + if (e.shiftKey && lastChecked && lastChecked !== cb){ + var checks = findRowCheckboxes(); + var start = checks.indexOf(lastChecked); + var end = checks.indexOf(cb); + if (start > -1 && end > -1){ + if (start > end){ var tmp = start; start = end; end = tmp; } + // mirror the checked state of the clicked checkbox + var newState = !cb.checked; + for (var i = start; i <= end; i++){ if (!checks[i].disabled) checks[i].checked = newState; } + updateSelectedCount(); + // ensure lastChecked follows the clicked checkbox + lastChecked = cb; + return; + } + } + + // Toggle single row checkbox as before + cb.checked = !cb.checked; + tr.classList.toggle('table-active', cb.checked); + updateSelectedCount(); + lastChecked = cb; }, true); table.addEventListener('change', function(e){ if (e.target && e.target.matches && e.target.matches('input[type="checkbox"]')) updateSelectedCount(); }, true);