feat(ext/popup): polished 2-column type-picker with glyph icons
This commit is contained in:
@@ -3,15 +3,19 @@
|
|||||||
|
|
||||||
import { navigate, getState, setState, escapeHtml, popOutToTab, isInTab } from '../../shared/state';
|
import { navigate, getState, setState, escapeHtml, popOutToTab, isInTab } from '../../shared/state';
|
||||||
import type { Item, ItemType } from '../../shared/types';
|
import type { Item, ItemType } from '../../shared/types';
|
||||||
|
import {
|
||||||
|
GLYPH_TYPE_LOGIN, GLYPH_TYPE_SECURE_NOTE, GLYPH_TYPE_TOTP,
|
||||||
|
GLYPH_TYPE_CARD, GLYPH_TYPE_IDENTITY, GLYPH_TYPE_KEY, GLYPH_TYPE_DOCUMENT,
|
||||||
|
} from '../../shared/glyphs';
|
||||||
|
|
||||||
const TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: string }> = [
|
const TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: string; description: string }> = [
|
||||||
{ type: 'login', icon: '🔑', label: 'login' },
|
{ type: 'login', icon: GLYPH_TYPE_LOGIN, label: 'Login', description: 'Username + password' },
|
||||||
{ type: 'secure_note', icon: '📝', label: 'secure note' },
|
{ type: 'secure_note', icon: GLYPH_TYPE_SECURE_NOTE, label: 'Secure Note', description: 'Encrypted text note' },
|
||||||
{ type: 'identity', icon: '👤', label: 'identity' },
|
{ type: 'identity', icon: GLYPH_TYPE_IDENTITY, label: 'Identity', description: 'Personal details' },
|
||||||
{ type: 'card', icon: '💳', label: 'card' },
|
{ type: 'card', icon: GLYPH_TYPE_CARD, label: 'Card', description: 'Credit / debit card' },
|
||||||
{ type: 'key', icon: '🔐', label: 'key' },
|
{ type: 'key', icon: GLYPH_TYPE_KEY, label: 'SSH / API Key', description: 'Keys and tokens' },
|
||||||
{ type: 'document', icon: '📄', label: 'document' },
|
{ type: 'document', icon: GLYPH_TYPE_DOCUMENT, label: 'Document', description: 'File attachment' },
|
||||||
{ type: 'totp', icon: '⏱️', label: 'totp' },
|
{ type: 'totp', icon: GLYPH_TYPE_TOTP, label: 'TOTP', description: '2FA authenticator' },
|
||||||
];
|
];
|
||||||
import * as login from './types/login';
|
import * as login from './types/login';
|
||||||
import * as secureNote from './types/secure-note';
|
import * as secureNote from './types/secure-note';
|
||||||
@@ -54,36 +58,36 @@ export function renderItemForm(app: HTMLElement, mode: 'add' | 'edit'): void {
|
|||||||
function renderTypeSelection(app: HTMLElement): void {
|
function renderTypeSelection(app: HTMLElement): void {
|
||||||
app.innerHTML = `
|
app.innerHTML = `
|
||||||
<div class="pad">
|
<div class="pad">
|
||||||
<div style="display:flex; align-items:center; gap:12px;">
|
<div style="display:flex; align-items:center; gap:12px; margin-bottom:12px;">
|
||||||
<button class="btn" id="back-btn">← back</button>
|
<button class="btn" id="back-btn">◂ back</button>
|
||||||
<h3 style="margin:0;">new item</h3>
|
<span style="font-size:14px; font-weight:600;">New item</span>
|
||||||
<span style="flex:1;"></span>
|
<span style="flex:1;"></span>
|
||||||
${isInTab() ? '' : '<button class="btn" id="popout-btn" title="Open in tab">⤴</button>'}
|
${isInTab() ? '' : '<button class="btn" id="popout-btn" title="Open in tab">⧉</button>'}
|
||||||
</div>
|
</div>
|
||||||
${isInTab() ? '<div class="form-subtitle">esc to cancel</div>' : '<div style="margin-bottom:16px;"></div>'}
|
<div class="type-card-grid">
|
||||||
<div class="type-select-list">
|
|
||||||
${TYPE_OPTIONS.map((opt) => `
|
${TYPE_OPTIONS.map((opt) => `
|
||||||
<button class="type-select-row" data-type="${opt.type}">
|
<button class="type-card" data-type="${opt.type}">
|
||||||
<span class="type-select-icon">${opt.icon}</span>
|
<span class="type-card__icon" aria-hidden="true">${opt.icon}</span>
|
||||||
<span>${escapeHtml(opt.label)}</span>
|
<span class="type-card__label">${escapeHtml(opt.label)}</span>
|
||||||
|
<span class="type-card__desc">${escapeHtml(opt.description)}</span>
|
||||||
</button>
|
</button>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="keyhints"><span><kbd>Esc</kbd> back</span></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
document.getElementById('back-btn')?.addEventListener('click', () => navigate('list'));
|
document.getElementById('back-btn')?.addEventListener('click', () => navigate('list'));
|
||||||
document.getElementById('popout-btn')?.addEventListener('click', popOutToTab);
|
document.getElementById('popout-btn')?.addEventListener('click', popOutToTab);
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') navigate('list');
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
document.querySelectorAll<HTMLButtonElement>('[data-type]').forEach((btn) => {
|
document.querySelectorAll<HTMLButtonElement>('[data-type]').forEach((btn) => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', () => {
|
||||||
const type = btn.dataset.type as ItemType;
|
const type = btn.dataset.type as ItemType;
|
||||||
setState({ newType: type });
|
setState({ newType: type });
|
||||||
if (type === 'login' || type === 'secure_note') {
|
|
||||||
renderItemForm(app, 'add');
|
renderItemForm(app, 'add');
|
||||||
} else {
|
|
||||||
popOutToTab();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1635,3 +1635,29 @@ textarea {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--text-muted, #8b949e);
|
color: var(--text-muted, #8b949e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.type-card-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: var(--bg-elevated, #161b22);
|
||||||
|
border: 1px solid var(--border-mid, #30363d);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-card:hover { border-color: var(--gold-base, #a88a4a); }
|
||||||
|
|
||||||
|
.type-card__icon { font-size: 20px; margin-bottom: 4px; }
|
||||||
|
.type-card__label { font-size: 12px; font-weight: 600; }
|
||||||
|
.type-card__desc { font-size: 10px; color: var(--text-muted, #8b949e); margin-top: 2px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user