feat(ext/popup): + New picker with all 7 item types (Document disabled)
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user