feat: add side-by-side live preview to form builder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Luca Oelfke 2026-02-13 15:47:49 +01:00
parent 8b9140ae6c
commit 33be56dcb1

View file

@ -1,5 +1,6 @@
import { App, Modal, Notice } from 'obsidian';
import { FormDefinition, FormField, FieldType } from '../types';
import { renderField } from './field-renderers';
const FIELD_TYPES: FieldType[] = [
'text',
@ -90,10 +91,15 @@ export class FormBuilderModal extends Modal {
}
});
// --- General settings ---
contentEl.createEl('h2', { text: 'Form Settings' });
// --- Two-column layout ---
const layout = contentEl.createDiv({ cls: 'ff-builder-layout' });
const generalEl = contentEl.createDiv({ cls: 'ff-builder-general' });
// === LEFT: Editor ===
const editorEl = layout.createDiv({ cls: 'ff-builder-editor' });
editorEl.createEl('h2', { text: 'Form Settings' });
const generalEl = editorEl.createDiv({ cls: 'ff-builder-general' });
// Name
this.addTextSetting(generalEl, 'Form Name', this.draft.name, (v) => {
@ -109,7 +115,7 @@ export class FormBuilderModal extends Modal {
(v) => {
this.pushSnapshot();
this.draft.mode = v as 'create' | 'update';
this.render(); // Re-render to show mode-specific settings
this.render();
},
);
@ -151,14 +157,14 @@ export class FormBuilderModal extends Modal {
);
}
// --- Fields section ---
contentEl.createEl('h3', { text: 'Fields' });
// Fields section
editorEl.createEl('h3', { text: 'Fields' });
const fieldsContainer = contentEl.createDiv({ cls: 'ff-builder-fields' });
const fieldsContainer = editorEl.createDiv({ cls: 'ff-builder-fields' });
this.renderFields(fieldsContainer);
// Add field button
const addBtn = contentEl.createEl('button', {
const addBtn = editorEl.createEl('button', {
cls: 'ff-builder-add-btn',
text: '+ Add Field',
});
@ -174,7 +180,40 @@ export class FormBuilderModal extends Modal {
this.render();
});
// --- Footer ---
// === RIGHT: Preview ===
const previewEl = layout.createDiv({ cls: 'ff-builder-preview' });
previewEl.createEl('h2', { text: 'Preview' });
const previewContent = previewEl.createDiv({ cls: 'ff-builder-preview-content' });
if (this.draft.fields.length === 0) {
previewContent.createDiv({
cls: 'ff-builder-preview-empty',
text: 'Add fields to see a preview.',
});
} else {
// Form title
previewContent.createEl('h3', {
text: this.draft.name || 'Untitled Form',
cls: 'ff-form-title',
});
// Render each field
const previewFields = previewContent.createDiv({ cls: 'ff-fields' });
for (const field of this.draft.fields) {
renderField(this.app, previewFields, field, field.defaultValue);
}
// Disabled submit button
const previewFooter = previewContent.createDiv({ cls: 'ff-form-footer' });
const previewSubmit = previewFooter.createEl('button', {
text: this.draft.mode === 'create' ? 'Create Note' : 'Update Frontmatter',
cls: 'mod-cta ff-submit-btn',
});
previewSubmit.setAttribute('disabled', '');
}
// --- Footer (full width, below grid) ---
const footer = contentEl.createDiv({ cls: 'ff-builder-footer' });
// Left side: undo/redo