';
if (showTrash) {
html += '
';
@@ -911,37 +904,42 @@ async function saveEdit() {
else { const d = await r.json(); toast('â ' + (d.error || 'Failed'), 'error'); }
} catch (e) { toast('â ī¸ Error', 'error'); }
}
-//======================== batch operations ==========================
-function toggleSelectMode() {
- selectMode = !selectMode;
- document.getElementById('selectBtn').textContent = selectMode ? 'â Deselect' : 'â Select';
- document.getElementById('selectBtn').classList.toggle('active', selectMode);
- if (!selectMode) {
- selectedIds.clear();
- hideBatchBar();
+//======================== batch selection ==========================
+function toggleSelectEntry(id, e) {
+ if (e?.shiftKey && lastSelectedId !== null) {
+ const ids = getFilteredEntries().map(x => x.id);
+ const i1 = ids.indexOf(lastSelectedId);
+ const i2 = ids.indexOf(id);
+ if (i1 > -1 && i2 > -1) {
+ const start = Math.min(i1, i2), end = Math.max(i1, i2);
+ for (let i = start; i <= end; i++) selectedIds.add(ids[i]);
+ }
+ } else if (e?.ctrlKey || e?.metaKey) {
+ if (selectedIds.has(id)) selectedIds.delete(id); else selectedIds.add(id);
+ } else {
+ if (selectedIds.size === 1 && selectedIds.has(id)) { selectedIds.clear(); }
+ else { selectedIds.clear(); selectedIds.add(id); }
}
+ lastSelectedId = id;
+ updateBatchBar();
render();
}
-function toggleSelectEntry(id) {
- if (selectedIds.has(id)) {
- selectedIds.delete(id);
- } else {
- selectedIds.add(id);
- }
- updateBatchBar();
+function clearSelection() {
+ selectedIds.clear();
+ lastSelectedId = null;
+ hideBatchBar();
+ render();
}
function updateBatchBar() {
const existing = document.getElementById('batchBar');
if (existing) existing.remove();
if (selectedIds.size === 0) return;
-
const bar = document.createElement('div');
bar.id = 'batchBar';
bar.className = 'batch-actions';
bar.innerHTML = `
${selectedIds.size} selected`;
-
if (showTrash) {
bar.innerHTML += `
@@ -956,7 +954,7 @@ function updateBatchBar() {
`;
}
- bar.innerHTML += `
`;
+ bar.innerHTML += `
`;
document.body.appendChild(bar);
}
@@ -968,21 +966,18 @@ function hideBatchBar() {
async function batchDelete() {
if (!confirm(`Move ${selectedIds.size} entries to trash?`)) return;
for (const id of selectedIds) await delEntry(id);
- selectedIds.clear();
- hideBatchBar();
+ clearSelection();
}
async function batchPermanentDelete() {
if (!confirm(`Permanently delete ${selectedIds.size} entries?`)) return;
for (const id of selectedIds) await permanentDelete(id);
- selectedIds.clear();
- hideBatchBar();
+ clearSelection();
}
async function batchRestore() {
for (const id of selectedIds) await restoreEntry(id);
- selectedIds.clear();
- hideBatchBar();
+ clearSelection();
}
async function batchMove() {
@@ -999,8 +994,7 @@ async function batchMove() {
});
}
}
- selectedIds.clear();
- hideBatchBar();
+ clearSelection();
loadEntries();
}
// ==================== ADD / DELETE ====================
@@ -1035,7 +1029,7 @@ async function addEntry() {
async function delEntry(id) {
try {
const r = await fetch(API + '/entries/' + id, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token, 'X-CSRF-Token': csrfToken } });
- if (r.ok) { order = order.filter(x => x != id); localStorage.setItem('entryOrder', JSON.stringify(order)); toast('đĻ Moved to trash'); await loadEntries(); playSound('delete'); }
+ if (r.ok) { selectedIds.delete(id); order = order.filter(x => x != id); localStorage.setItem('entryOrder', JSON.stringify(order)); toast('đĻ Moved to trash'); await loadEntries(); playSound('delete'); }
} catch (e) { toast('Error', 'error'); }
}
@@ -1090,7 +1084,7 @@ function highlightText(text, query) {
function showShortcutsHelp() {
const overlay = document.createElement('div');
overlay.className = 'custom-modal-overlay show';
- overlay.innerHTML = `
â¨ī¸ Keyboard Shortcuts
Ctrl+NNew entryCtrl+FSearchCtrl+TToggle trashCtrl+LLock vaultCtrl+SSave entryEscClose modal / settings?Show this help
`;
+ overlay.innerHTML = `
â¨ī¸ Keyboard Shortcuts
Ctrl+NNew entryCtrl+FSearchCtrl+TToggle trashCtrl+LLock vaultCtrl+SSave entryEscClose modal / deselect?Show this help
đĄ Click any entry to select, Shift+click for range, Ctrl+click to toggle
`;
document.body.appendChild(overlay);
overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
}
@@ -1123,6 +1117,7 @@ if (token && curUser) {
}
['click', 'keypress', 'scroll', 'mousemove'].forEach(e => document.addEventListener(e, () => { if (token) resetIdle(); }));
+document.getElementById('entriesContainer').addEventListener('click', function(e) { if (e.target === this || (selectedIds.size > 0 && !e.target.closest('.entry-card,.entry-row,.entry-compact,.table-row-drag'))) { clearSelection(); } });
document.addEventListener('keypress', e => { if (e.key === 'Enter') { if (document.getElementById('passwordInput') === document.activeElement) addEntry(); else if (document.getElementById('loginPassword') === document.activeElement) login(); else if (document.getElementById('regPassword') === document.activeElement) register(); } });
document.getElementById('genModal').addEventListener('click', e => { if (e.target === e.currentTarget) closeGen(); });
document.getElementById('editModal').addEventListener('click', e => { if (e.target === e.currentTarget) closeEdit(); });
@@ -1133,6 +1128,7 @@ document.addEventListener('keydown', function(e) {
// Escape â close any open modal or settings
if (e.key === 'Escape') {
+ if (selectedIds.size > 0) { clearSelection(); return; }
if (!document.getElementById('settingsMenu').classList.contains('hidden')) {
document.getElementById('settingsMenu').classList.add('hidden');
return;