Fix Shift+arrow range selection: track anchor (fixed) and focus (moving) separately

Previous approach always started from the lowest-indexed selected entry, causing the range to snap to anchor on arrow reversal. Now arrowFocus tracks the moving end; arrowAnchor stays fixed. Non-Shift arrow resets both to the new position.
This commit is contained in:
2026-05-09 23:36:39 +01:00
parent 0a0e380b95
commit 194c11ece2
+9 -4
View File
@@ -23,6 +23,7 @@ let showTrash = false;
let selectedIds = new Set(); let selectedIds = new Set();
let lastSelectedId = null; let lastSelectedId = null;
let arrowAnchor = -1; let arrowAnchor = -1;
let arrowFocus = -1;
let rectState = { active: false, startX: 0, startY: 0, el: null, started: false }; let rectState = { active: false, startX: 0, startY: 0, el: null, started: false };
// ==================== SOUND ==================== // ==================== SOUND ====================
@@ -1083,6 +1084,7 @@ function toggleSelectEntry(id, e) {
} }
lastSelectedId = id; lastSelectedId = id;
arrowAnchor = -1; arrowAnchor = -1;
arrowFocus = -1;
updateBatchBar(); updateBatchBar();
render(); render();
} }
@@ -1091,6 +1093,7 @@ function clearSelection() {
selectedIds.clear(); selectedIds.clear();
lastSelectedId = null; lastSelectedId = null;
arrowAnchor = -1; arrowAnchor = -1;
arrowFocus = -1;
hideBatchBar(); hideBatchBar();
render(); render();
} }
@@ -1426,8 +1429,8 @@ document.addEventListener('keydown', function(e) {
const filtered = getFilteredEntries(); const filtered = getFilteredEntries();
if (!filtered.length) return; if (!filtered.length) return;
const isNext = e.key === 'ArrowDown' || e.key === 'ArrowRight'; const isNext = e.key === 'ArrowDown' || e.key === 'ArrowRight';
let idx = -1; let idx = arrowFocus >= 0 ? arrowFocus : -1;
if (selectedIds.size > 0) { if (idx < 0 && selectedIds.size > 0) {
const firstId = [...selectedIds][0]; const firstId = [...selectedIds][0];
idx = filtered.findIndex(e => e.id == firstId); idx = filtered.findIndex(e => e.id == firstId);
} }
@@ -1440,13 +1443,15 @@ document.addEventListener('keydown', function(e) {
} }
if (e.shiftKey) { if (e.shiftKey) {
if (arrowAnchor < 0) arrowAnchor = idx; if (arrowAnchor < 0) arrowAnchor = idx;
const start = Math.min(arrowAnchor, idx), end = Math.max(arrowAnchor, idx); arrowFocus = idx;
const start = Math.min(arrowAnchor, arrowFocus), end = Math.max(arrowAnchor, arrowFocus);
selectedIds.clear(); selectedIds.clear();
for (let i = start; i <= end; i++) selectedIds.add(filtered[i].id); for (let i = start; i <= end; i++) selectedIds.add(filtered[i].id);
} else { } else {
selectedIds.clear(); selectedIds.clear();
selectedIds.add(filtered[idx].id); selectedIds.add(filtered[idx].id);
if (arrowAnchor < 0) arrowAnchor = idx; arrowAnchor = idx;
arrowFocus = idx;
} }
updateBatchBar(); render(); playSound('click'); updateBatchBar(); render(); playSound('click');
return; return;