Files
relicario/extension/src/popup/components/item-form.ts
adlee-was-taken a28b456191 feat(ext/login): add surface flag for two-column fullscreen form
renderForm() takes an optional { surface: 'popup' | 'fullscreen' }
parameter. When 'fullscreen', the Identity and Credentials field
groups render as glass cards inside a .form-grid (two columns,
stacks at <=720px). Popup keeps its single-column layout.
2026-05-02 15:01:35 -04:00

101 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// Typed-item add/edit form dispatcher. Each type's renderForm lives in
/// its own module under ./types/. Document stays "coming soon" until γ.
import { navigate, getState, setState, escapeHtml, popOutToTab, isInTab } from '../../shared/state';
import type { Item, ItemType } from '../../shared/types';
const TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: 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: 'document', icon: '📄', label: 'document' },
{ type: 'totp', icon: '⏱️', label: 'totp' },
];
import * as login from './types/login';
import * as secureNote from './types/secure-note';
import * as identity from './types/identity';
import * as card from './types/card';
import * as key from './types/key';
import * as totp from './types/totp';
import * as documentType from './types/document';
export function renderItemForm(app: HTMLElement, mode: 'add' | 'edit'): void {
login.teardown();
secureNote.teardown();
identity.teardown();
card.teardown();
key.teardown();
totp.teardown();
documentType.teardown();
const state = getState();
const existing = mode === 'edit' ? state.selectedItem : null;
// Show type selection for new items when no type is pre-selected
if (mode === 'add' && !state.newType) {
renderTypeSelection(app);
return;
}
const type: ItemType = existing?.type ?? state.newType ?? 'login';
switch (type) {
case 'login': return login.renderForm(app, mode, existing, { surface: isInTab() ? 'fullscreen' : 'popup' });
case 'secure_note': return secureNote.renderForm(app, mode, existing);
case 'identity': return identity.renderForm(app, mode, existing);
case 'card': return card.renderForm(app, mode, existing);
case 'key': return key.renderForm(app, mode, existing);
case 'totp': return totp.renderForm(app, mode, existing);
case 'document': return documentType.renderForm(app, mode, existing);
}
}
function renderTypeSelection(app: HTMLElement): void {
app.innerHTML = `
<div class="pad">
<div style="display:flex; align-items:center; gap:12px;">
<button class="btn" id="back-btn">← back</button>
<h3 style="margin:0;">new item</h3>
<span style="flex:1;"></span>
${isInTab() ? '' : '<button class="btn" id="popout-btn" title="Open in tab">⤴</button>'}
</div>
${isInTab() ? '<div class="form-subtitle">esc to cancel</div>' : '<div style="margin-bottom:16px;"></div>'}
<div class="type-select-list">
${TYPE_OPTIONS.map((opt) => `
<button class="type-select-row" data-type="${opt.type}">
<span class="type-select-icon">${opt.icon}</span>
<span>${escapeHtml(opt.label)}</span>
</button>
`).join('')}
</div>
</div>
`;
document.getElementById('back-btn')?.addEventListener('click', () => navigate('list'));
document.getElementById('popout-btn')?.addEventListener('click', popOutToTab);
document.querySelectorAll<HTMLButtonElement>('[data-type]').forEach((btn) => {
btn.addEventListener('click', () => {
const type = btn.dataset.type as ItemType;
setState({ newType: type });
if (type === 'login' || type === 'secure_note') {
renderItemForm(app, 'add');
} else {
popOutToTab();
}
});
});
}
function renderComingSoon(app: HTMLElement, type: ItemType): void {
app.innerHTML = `
<div class="pad">
<div class="detail-title" style="margin-bottom:16px;">${type.replace('_', ' ')}</div>
<p class="muted">Editing <strong>${type}</strong> items is not available yet.</p>
<div class="form-actions"><button class="btn" id="back-btn">back</button></div>
</div>
`;
document.getElementById('back-btn')?.addEventListener('click', () => navigate('list'));
}