04/17/2026 update add item modal disappears when lose focus on it

This commit is contained in:
2026-04-17 17:18:02 -04:00
parent 802e29857f
commit d6715e8810
+27 -27
View File
@@ -187,11 +187,11 @@ const Vault = (() => {
function getSortedItems(items) {
const sorted = [...items];
switch (_sortOrder) {
case 'name-asc': sorted.sort((a,b) => a.name.localeCompare(b.name)); break;
case 'name-desc': sorted.sort((a,b) => b.name.localeCompare(a.name)); break;
case 'date-desc': sorted.sort((a,b) => new Date(b.created_at) - new Date(a.created_at)); break;
case 'date-asc': sorted.sort((a,b) => new Date(a.created_at) - new Date(b.created_at)); break;
case 'folder': sorted.sort((a,b) => folderName(a.folder_id).localeCompare(folderName(b.folder_id)) || a.name.localeCompare(b.name)); break;
case 'name-asc': sorted.sort((a, b) => a.name.localeCompare(b.name)); break;
case 'name-desc': sorted.sort((a, b) => b.name.localeCompare(a.name)); break;
case 'date-desc': sorted.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); break;
case 'date-asc': sorted.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); break;
case 'folder': sorted.sort((a, b) => folderName(a.folder_id).localeCompare(folderName(b.folder_id)) || a.name.localeCompare(b.name)); break;
}
return sorted;
}
@@ -238,7 +238,7 @@ const Vault = (() => {
li.className = 'vault-item';
li.dataset.id = item.id;
const iconMap = { password:'🔑', note:'📝', card:'💳', bank:'🏦', address:'🏠', ssn:'🪪', passkey:'🔐' };
const iconMap = { password: '🔑', note: '📝', card: '💳', bank: '🏦', address: '🏠', ssn: '🪪', passkey: '🔐' };
const icon = iconMap[item.item_type] || '🔑';
let subText = '';
@@ -246,7 +246,7 @@ const Vault = (() => {
switch (item.item_type) {
case 'password': subText = item.plain.username || item.plain.url || ''; break;
case 'note': subText = (item.plain.note_body || '').slice(0, 60); break;
case 'card': subText = item.plain.card_number ? '•••• ' + String(item.plain.card_number).replace(/\s/g,'').slice(-4) : ''; break;
case 'card': subText = item.plain.card_number ? '•••• ' + String(item.plain.card_number).replace(/\s/g, '').slice(-4) : ''; break;
case 'bank': subText = item.plain.bank_name || ''; break;
case 'address': subText = [item.plain.first_name, item.plain.last_name].filter(Boolean).join(' '); break;
case 'ssn': subText = '•••-••-••••'; break;
@@ -254,7 +254,7 @@ const Vault = (() => {
}
const showCopyUser = item.item_type === 'password';
const showCopyPass = ['password','card','bank','ssn'].includes(item.item_type);
const showCopyPass = ['password', 'card', 'bank', 'ssn'].includes(item.item_type);
const showLaunch = item.item_type === 'password' && item.plain?.url;
li.innerHTML = `
@@ -328,7 +328,7 @@ const Vault = (() => {
const old = pwItems.filter(i => new Date(i.created_at).getTime() < cutoff);
const total = pwItems.length;
const score = Math.max(0, Math.round(100 - (weak.length/total)*40 - (reused.length/total)*30 - (old.length/total)*15));
const score = Math.max(0, Math.round(100 - (weak.length / total) * 40 - (reused.length / total) * 30 - (old.length / total) * 15));
const cls = score >= 80 ? 'score-good' : score >= 50 ? 'score-fair' : 'score-poor';
const label = score >= 80 ? 'Good' : score >= 50 ? 'Fair' : 'Needs attention';
@@ -912,7 +912,7 @@ const Vault = (() => {
// clamp to 5 bands
const band = Math.min(5, Math.ceil(score / 7 * 5));
const labels = ['', 'Very Weak', 'Weak', 'Fair', 'Strong', 'Very Strong'];
const clses = ['', 'strength-1','strength-2','strength-3','strength-4','strength-5'];
const clses = ['', 'strength-1', 'strength-2', 'strength-3', 'strength-4', 'strength-5'];
return { score: band, label: labels[band], cls: clses[band] };
}
@@ -1017,7 +1017,7 @@ const Vault = (() => {
lengthDisplay.textContent = lengthSlider.value;
refreshGen();
});
['gen-upper','gen-lower','gen-digits','gen-symbols'].forEach(id => {
['gen-upper', 'gen-lower', 'gen-digits', 'gen-symbols'].forEach(id => {
document.getElementById(id).addEventListener('change', refreshGen);
});
document.getElementById('btn-regenerate').addEventListener('click', refreshGen);
@@ -1269,7 +1269,7 @@ const Vault = (() => {
// ── Item Modal ────────────────────────────────────────────────────────────
const TYPE_LABELS = { password:'🔑 Password', note:'📝 Secure Note', card:'💳 Payment Card', bank:'🏦 Bank Account', address:'🏠 Address', ssn:'🪪 Identity (SSN)' };
const TYPE_LABELS = { password: '🔑 Password', note: '📝 Secure Note', card: '💳 Payment Card', bank: '🏦 Bank Account', address: '🏠 Address', ssn: '🪪 Identity (SSN)' };
function switchModalType(type) {
document.querySelectorAll('.type-fields').forEach(el => {
@@ -1307,12 +1307,12 @@ const Vault = (() => {
if (item.plain) {
const p = item.plain;
switch (t) {
case 'password': setVal('field-url',p.url); setVal('field-username',p.username); setVal('field-password',p.password); setVal('field-notes',p.notes); break;
case 'note': setVal('field-note-body',p.note_body); break;
case 'card': setVal('field-cardholder',p.cardholder_name); setVal('field-card-number',p.card_number); setVal('field-expiry-month',p.expiry_month); setVal('field-expiry-year',p.expiry_year); setVal('field-cvv',p.cvv); setVal('field-notes',p.notes); break;
case 'bank': setVal('field-bank-name',p.bank_name); setVal('field-account-type',p.account_type); setVal('field-routing',p.routing_number); setVal('field-account-number',p.account_number); setVal('field-notes',p.notes); break;
case 'address': ['first_name','last_name','company','address_line','city','state','zip','country','phone','email'].forEach(k => setVal('field-'+k.replace(/_/g,'-'), p[k])); setVal('field-notes',p.notes); break;
case 'ssn': setVal('field-ssn-number',p.ssn_number); setVal('field-notes',p.notes); break;
case 'password': setVal('field-url', p.url); setVal('field-username', p.username); setVal('field-password', p.password); setVal('field-notes', p.notes); break;
case 'note': setVal('field-note-body', p.note_body); break;
case 'card': setVal('field-cardholder', p.cardholder_name); setVal('field-card-number', p.card_number); setVal('field-expiry-month', p.expiry_month); setVal('field-expiry-year', p.expiry_year); setVal('field-cvv', p.cvv); setVal('field-notes', p.notes); break;
case 'bank': setVal('field-bank-name', p.bank_name); setVal('field-account-type', p.account_type); setVal('field-routing', p.routing_number); setVal('field-account-number', p.account_number); setVal('field-notes', p.notes); break;
case 'address': ['first_name', 'last_name', 'company', 'address_line', 'city', 'state', 'zip', 'country', 'phone', 'email'].forEach(k => setVal('field-' + k.replace(/_/g, '-'), p[k])); setVal('field-notes', p.notes); break;
case 'ssn': setVal('field-ssn-number', p.ssn_number); setVal('field-notes', p.notes); break;
}
}
}
@@ -1395,7 +1395,7 @@ const Vault = (() => {
method: 'POST',
headers: { 'Authorization': `Bearer ${sessionStorage.getItem('access_token') || ''}`, 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken() },
body: JSON.stringify({ refresh_token: refreshToken }),
}).catch(() => {});
}).catch(() => { });
VaultSession.clear();
SharingSession.clear();
sessionStorage.removeItem('access_token');
@@ -1407,15 +1407,15 @@ const Vault = (() => {
// ── Utilities ─────────────────────────────────────────────────────────────
function copyToClipboard(text, msg = 'Copied') {
navigator.clipboard.writeText(text).then(() => showToast(msg)).catch(() => {});
navigator.clipboard.writeText(text).then(() => showToast(msg)).catch(() => { });
}
function escHtml(str) {
return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function itemIcon(type) {
return { password:'🔑', note:'📝', card:'💳', bank:'🏦', address:'🏠', ssn:'🪪' }[type] || '🔑';
return { password: '🔑', note: '📝', card: '💳', bank: '🏦', address: '🏠', ssn: '🪪' }[type] || '🔑';
}
function showToast(message, type = 'success') {
@@ -1524,7 +1524,7 @@ const Vault = (() => {
document.querySelectorAll('[data-type-filter]').forEach(el => {
el.addEventListener('click', () => {
const type = el.dataset.typeFilter;
const labels = { password:'Passwords', note:'Secure Notes', card:'Payment Cards', bank:'Bank Accounts', address:'Addresses', ssn:'Identities' };
const labels = { password: 'Passwords', note: 'Secure Notes', card: 'Payment Cards', bank: 'Bank Accounts', address: 'Addresses', ssn: 'Identities' };
_activeFilter = { type: 'itemType', value: type };
switchView('vault');
document.querySelectorAll('.sidebar-item').forEach(s => s.classList.remove('active'));
@@ -1534,8 +1534,8 @@ const Vault = (() => {
});
});
document.getElementById('item-modal')?.addEventListener('click', e => { if (e.target.id === 'item-modal') closeModal(); });
document.addEventListener('keydown', e => { if (e.key === 'Escape') { closeModal(); closeModal('share-modal'); closeModal('emergency-modal'); closeModal('settings-modal'); closeModal('detail-modal'); closeModal('gen-modal'); } });
document.getElementById('item-modal')?.addEventListener('click', e => { /* backdrop click intentionally disabled — modal closes only via Cancel or Save */ });
document.addEventListener('keydown', e => { if (e.key === 'Escape') { closeModal('share-modal'); closeModal('emergency-modal'); closeModal('settings-modal'); closeModal('detail-modal'); closeModal('gen-modal'); } });
// Detail modal (shared item view)
const closeDetailModal = () => closeModal('detail-modal');
@@ -1544,8 +1544,8 @@ const Vault = (() => {
document.getElementById('detail-modal')?.addEventListener('click', e => { if (e.target.id === 'detail-modal') closeDetailModal(); });
document.getElementById('field-card-number')?.addEventListener('input', e => {
let v = e.target.value.replace(/\D/g,'').slice(0,16);
e.target.value = v.replace(/(.{4})/g,'$1 ').trim();
let v = e.target.value.replace(/\D/g, '').slice(0, 16);
e.target.value = v.replace(/(.{4})/g, '$1 ').trim();
});
// Settings modal