refactor(ext/vault): extract vault-form-wrapper.ts (Plan C Phase 4)
Moves renderFormWrapped (sticky save bar + header + dirty-state wiring), the SAVE_HINT/isMac consts, and the __test__ export out of vault.ts into vault-form-wrapper.ts, taking the VaultController ctx. Repoints the source-text form-wrapper test to read the new module. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ import {
|
||||
openDrawer, closeDrawer, renderDrawer, selectItemForDrawer,
|
||||
ensureDrawerClosedForRoute,
|
||||
} from './vault-drawer';
|
||||
import { renderFormWrapped } from './vault-form-wrapper';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
@@ -187,71 +188,6 @@ registerHost({
|
||||
openVaultTab: () => {},
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Platform-aware save hint
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const isMac = navigator.platform.toLowerCase().includes('mac');
|
||||
const SAVE_HINT = isMac ? '⌘+S to save' : 'Ctrl+S to save';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fullscreen form wrapper — sticky save bar + scrollable content + header
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function renderFormWrapped(app: HTMLElement, mode: 'add' | 'edit'): void {
|
||||
const itemType = state.selectedItem?.type ?? state.newType ?? 'login';
|
||||
const typeLabelText = itemType.replace('_', ' ');
|
||||
const titleText = mode === 'add' ? `new ${typeLabelText}` : `edit ${typeLabelText}`;
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'form-pane';
|
||||
wrapper.innerHTML = `
|
||||
<div class="fullscreen-form-header">
|
||||
<div>
|
||||
<div class="title">${titleText}</div>
|
||||
<div class="sub" id="form-dirty-sub">no changes</div>
|
||||
</div>
|
||||
<div class="hint">${SAVE_HINT}</div>
|
||||
</div>
|
||||
<div class="form-scroll" id="form-scroll"></div>
|
||||
<div class="sticky-save-bar">
|
||||
<button class="btn-secondary" id="form-cancel">cancel</button>
|
||||
<button class="btn-primary" id="form-save">save</button>
|
||||
</div>
|
||||
`;
|
||||
// Remove pane padding so form-pane can fill height cleanly
|
||||
app.style.padding = '0';
|
||||
app.style.overflow = 'hidden';
|
||||
app.replaceChildren(wrapper);
|
||||
|
||||
const scrollEl = wrapper.querySelector('#form-scroll') as HTMLElement;
|
||||
renderItemForm(scrollEl, mode);
|
||||
|
||||
const subEl = wrapper.querySelector('#form-dirty-sub') as HTMLElement;
|
||||
let isDirty = false;
|
||||
const markDirty = () => {
|
||||
if (isDirty) return;
|
||||
isDirty = true;
|
||||
subEl.textContent = 'unsaved · esc to cancel';
|
||||
};
|
||||
const markClean = () => {
|
||||
isDirty = false;
|
||||
subEl.textContent = 'no changes';
|
||||
};
|
||||
scrollEl.addEventListener('input', markDirty, true);
|
||||
scrollEl.addEventListener('change', markDirty, true);
|
||||
|
||||
wrapper.querySelector('#form-cancel')?.addEventListener('click', () => {
|
||||
markClean();
|
||||
(scrollEl.querySelector('#cancel-btn') as HTMLButtonElement | null)?.click();
|
||||
});
|
||||
wrapper.querySelector('#form-save')?.addEventListener('click', () => {
|
||||
markClean();
|
||||
(scrollEl.querySelector('#save-btn') as HTMLButtonElement | null)?.click();
|
||||
});
|
||||
}
|
||||
|
||||
export const __test__ = { renderFormWrapped };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pane rendering — delegates to shared popup components
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -297,13 +233,13 @@ function renderPane(): void {
|
||||
// Use the form wrapper (sticky bar + header) when a type is already chosen.
|
||||
// Without a type the type-selection screen renders — no sticky bar needed.
|
||||
if (state.newType) {
|
||||
renderFormWrapped(pane, 'add');
|
||||
renderFormWrapped(ctx, pane, 'add');
|
||||
} else {
|
||||
renderItemForm(pane, 'add');
|
||||
}
|
||||
break;
|
||||
case 'edit':
|
||||
renderFormWrapped(pane, 'edit');
|
||||
renderFormWrapped(ctx, pane, 'edit');
|
||||
break;
|
||||
case 'trash':
|
||||
renderTrash(pane);
|
||||
|
||||
Reference in New Issue
Block a user