feat: add sidebar view with form list
ItemView that displays all forms with mode badges (C/U), click to open, settings gear button, and empty state. Uses SidebarPluginRef interface to avoid circular dependency with main.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
092c960a9c
commit
5eb79fbf08
1 changed files with 94 additions and 0 deletions
94
src/ui/form-sidebar.ts
Normal file
94
src/ui/form-sidebar.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { ItemView, WorkspaceLeaf, setIcon } from 'obsidian';
|
||||||
|
import { FormStore } from '../core/form-store';
|
||||||
|
|
||||||
|
export const VIEW_TYPE_FORMFIRE_SIDEBAR = 'formfire-sidebar';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for the subset of FormfirePlugin that the sidebar needs.
|
||||||
|
* Avoids circular dependency with main.ts.
|
||||||
|
*/
|
||||||
|
export interface SidebarPluginRef {
|
||||||
|
store: FormStore;
|
||||||
|
openForm: (id: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FormfireSidebarView extends ItemView {
|
||||||
|
private pluginRef: SidebarPluginRef;
|
||||||
|
|
||||||
|
constructor(leaf: WorkspaceLeaf, pluginRef: SidebarPluginRef) {
|
||||||
|
super(leaf);
|
||||||
|
this.pluginRef = pluginRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewType(): string {
|
||||||
|
return VIEW_TYPE_FORMFIRE_SIDEBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayText(): string {
|
||||||
|
return 'Formfire';
|
||||||
|
}
|
||||||
|
|
||||||
|
getIcon(): string {
|
||||||
|
return 'file-input';
|
||||||
|
}
|
||||||
|
|
||||||
|
async onOpen(): Promise<void> {
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onClose(): Promise<void> {
|
||||||
|
this.contentEl.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): void {
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
|
||||||
|
const container = contentEl.createDiv({ cls: 'ff-sidebar' });
|
||||||
|
|
||||||
|
// Header
|
||||||
|
const header = container.createDiv({ cls: 'ff-sidebar-header' });
|
||||||
|
header.createSpan({ text: 'Forms', cls: 'ff-sidebar-title' });
|
||||||
|
|
||||||
|
const settingsBtn = header.createDiv({ cls: 'ff-sidebar-settings' });
|
||||||
|
setIcon(settingsBtn, 'settings');
|
||||||
|
settingsBtn.setAttribute('aria-label', 'Formfire settings');
|
||||||
|
settingsBtn.addEventListener('click', () => {
|
||||||
|
// Open Obsidian settings and navigate to the Formfire tab
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const appAny = this.app as any;
|
||||||
|
appAny.setting.open();
|
||||||
|
appAny.setting.openTabById('formfire');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Form list
|
||||||
|
const forms = this.pluginRef.store.getAll();
|
||||||
|
|
||||||
|
if (forms.length === 0) {
|
||||||
|
container.createDiv({
|
||||||
|
cls: 'ff-sidebar-empty',
|
||||||
|
text: 'No forms yet. Open settings to create one.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listEl = container.createDiv({ cls: 'ff-sidebar-list' });
|
||||||
|
|
||||||
|
for (const form of forms) {
|
||||||
|
const item = listEl.createDiv({ cls: 'ff-sidebar-item' });
|
||||||
|
|
||||||
|
const badgeCls =
|
||||||
|
form.mode === 'create' ? 'ff-badge-create' : 'ff-badge-update';
|
||||||
|
item.createSpan({
|
||||||
|
cls: `ff-sidebar-badge ${badgeCls}`,
|
||||||
|
text: form.mode === 'create' ? 'C' : 'U',
|
||||||
|
});
|
||||||
|
|
||||||
|
item.createSpan({ cls: 'ff-sidebar-name', text: form.name });
|
||||||
|
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
this.pluginRef.openForm(form.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue