feat(ext/popup): + New picker with all 7 item types (Document disabled)

This commit is contained in:
adlee-was-taken
2026-04-23 23:07:33 -04:00
parent 3c0b4c1589
commit 23759dc163

View File

@@ -67,7 +67,10 @@ export function renderItemList(app: HTMLElement): void {
setState({ searchQuery: searchInput.value, selectedIndex: 0 }); setState({ searchQuery: searchInput.value, selectedIndex: 0 });
}); });
document.getElementById('new-btn')?.addEventListener('click', () => navigate('add')); document.getElementById('new-btn')?.addEventListener('click', (e) => {
e.stopPropagation();
showNewTypePicker(e.currentTarget as HTMLElement);
});
document.getElementById('sync-btn')?.addEventListener('click', async () => { document.getElementById('sync-btn')?.addEventListener('click', async () => {
setState({ loading: true, error: null }); setState({ loading: true, error: null });
@@ -215,3 +218,92 @@ function handleListKeydown(e: KeyboardEvent): void {
return; return;
} }
} }
// ----------------------------------------------------------------------
// New-item type picker popover
// ----------------------------------------------------------------------
const NEW_TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: string; disabled?: boolean; tooltip?: string }> = [
{ type: 'login', icon: '🔑', label: 'login' },
{ type: 'secure_note', icon: '📝', label: 'secure note' },
{ type: 'identity', icon: '🪪', label: 'identity' },
{ type: 'card', icon: '💳', label: 'card' },
{ type: 'key', icon: '🗝', label: 'key' },
{ type: 'totp', icon: '⏱', label: 'totp' },
{ type: 'document', icon: '📄', label: 'document', disabled: true, tooltip: 'coming in γ — needs attachment upload' },
];
function showNewTypePicker(anchor: HTMLElement): void {
document.querySelectorAll('.new-type-picker').forEach((el) => el.remove());
const picker = document.createElement('div');
picker.className = 'new-type-picker';
Object.assign(picker.style, {
position: 'absolute',
background: '#161b22',
border: '1px solid #30363d',
borderRadius: '6px',
boxShadow: '0 4px 12px rgba(0,0,0,0.4)',
padding: '4px',
minWidth: '160px',
zIndex: '999999',
fontSize: '12px',
});
const rect = anchor.getBoundingClientRect();
picker.style.top = `${rect.bottom + 4}px`;
picker.style.left = `${rect.left}px`;
for (const opt of NEW_TYPE_OPTIONS) {
const row = document.createElement('div');
Object.assign(row.style, {
padding: '6px 10px',
cursor: opt.disabled ? 'not-allowed' : 'pointer',
color: opt.disabled ? '#484f58' : '#c9d1d9',
borderRadius: '4px',
display: 'flex', alignItems: 'center', gap: '8px',
});
if (opt.tooltip) row.title = opt.tooltip;
const iconSpan = document.createElement('span');
Object.assign(iconSpan.style, { fontSize: '14px', width: '16px', display: 'inline-block', textAlign: 'center' });
iconSpan.textContent = opt.icon;
const labelSpan = document.createElement('span');
labelSpan.textContent = opt.label;
row.appendChild(iconSpan);
row.appendChild(labelSpan);
if (!opt.disabled) {
row.addEventListener('mouseenter', () => { row.style.background = '#21262d'; });
row.addEventListener('mouseleave', () => { row.style.background = 'transparent'; });
row.addEventListener('click', (ev) => {
ev.stopPropagation();
picker.remove();
document.removeEventListener('click', closeOnOutside);
document.removeEventListener('keydown', closeOnEsc);
setState({ newType: opt.type });
navigate('add');
});
}
picker.appendChild(row);
}
document.body.appendChild(picker);
const closeOnOutside = (ev: MouseEvent) => {
if (!picker.contains(ev.target as Node)) {
picker.remove();
document.removeEventListener('click', closeOnOutside);
document.removeEventListener('keydown', closeOnEsc);
}
};
const closeOnEsc = (ev: KeyboardEvent) => {
if (ev.key === 'Escape') {
picker.remove();
document.removeEventListener('click', closeOnOutside);
document.removeEventListener('keydown', closeOnEsc);
}
};
setTimeout(() => {
document.addEventListener('click', closeOnOutside);
document.addEventListener('keydown', closeOnEsc);
}, 0);
}