Enhance Interview Wizard Modal with improved preview and editing functionality
- Added a "Back to Edit" button wrapper in preview mode for easier navigation back to editing. - Updated preview rendering logic to ensure the back button visibility aligns with the current mode. - Enhanced textarea handling to retrieve and update draft values more effectively when toggling between preview and edit modes. - Improved error handling and fallback rendering for the Markdown preview, ensuring a better user experience during content display. - Refactored existing rendering methods to maintain structure while clearing content, enhancing performance and reliability.
This commit is contained in:
parent
070cb853a9
commit
b5f2c8fc67
|
|
@ -325,6 +325,35 @@ export class InterviewWizardModal extends Modal {
|
||||||
previewContainer.style.borderRadius = "4px";
|
previewContainer.style.borderRadius = "4px";
|
||||||
previewContainer.style.background = "var(--background-primary)";
|
previewContainer.style.background = "var(--background-primary)";
|
||||||
previewContainer.style.overflowY = "auto";
|
previewContainer.style.overflowY = "auto";
|
||||||
|
previewContainer.style.position = "relative";
|
||||||
|
|
||||||
|
// Add "Back to Edit" button wrapper (outside preview content, so it doesn't get cleared)
|
||||||
|
const backToEditWrapper = editorContainer.createEl("div", {
|
||||||
|
cls: "preview-back-button-wrapper",
|
||||||
|
});
|
||||||
|
backToEditWrapper.style.display = isPreviewMode ? "block" : "none";
|
||||||
|
backToEditWrapper.style.position = "absolute";
|
||||||
|
backToEditWrapper.style.top = "0.5em";
|
||||||
|
backToEditWrapper.style.right = "0.5em";
|
||||||
|
backToEditWrapper.style.zIndex = "20";
|
||||||
|
|
||||||
|
const backToEditBtn = backToEditWrapper.createEl("button", {
|
||||||
|
text: "✏️ Zurück zum Bearbeiten",
|
||||||
|
cls: "mod-cta",
|
||||||
|
});
|
||||||
|
backToEditBtn.onclick = () => {
|
||||||
|
// Get current value from stored input values
|
||||||
|
const currentValue = this.currentInputValues.get(step.key) || existingValue;
|
||||||
|
// Update stored value
|
||||||
|
this.currentInputValues.set(step.key, String(currentValue));
|
||||||
|
this.state.collectedData.set(step.key, currentValue);
|
||||||
|
|
||||||
|
// Toggle preview mode off
|
||||||
|
this.previewMode.set(step.key, false);
|
||||||
|
|
||||||
|
// Re-render to show editor
|
||||||
|
this.renderStep();
|
||||||
|
};
|
||||||
|
|
||||||
// Editor container
|
// Editor container
|
||||||
const textEditorContainer = editorContainer.createEl("div", {
|
const textEditorContainer = editorContainer.createEl("div", {
|
||||||
|
|
@ -376,10 +405,36 @@ export class InterviewWizardModal extends Modal {
|
||||||
if (textarea) {
|
if (textarea) {
|
||||||
const toolbar = createMarkdownToolbar(
|
const toolbar = createMarkdownToolbar(
|
||||||
textarea,
|
textarea,
|
||||||
() => {
|
async () => {
|
||||||
|
// Get current value from textarea before toggling
|
||||||
|
let currentValue = textarea.value;
|
||||||
|
// If textarea is empty, try to get from stored values
|
||||||
|
if (!currentValue || currentValue.trim() === "") {
|
||||||
|
currentValue = this.currentInputValues.get(step.key) || existingValue || "";
|
||||||
|
}
|
||||||
|
// Update stored value
|
||||||
|
this.currentInputValues.set(step.key, currentValue);
|
||||||
|
this.state.collectedData.set(step.key, currentValue);
|
||||||
|
|
||||||
|
// Toggle preview mode
|
||||||
const newPreviewMode = !this.previewMode.get(step.key);
|
const newPreviewMode = !this.previewMode.get(step.key);
|
||||||
this.previewMode.set(step.key, newPreviewMode);
|
this.previewMode.set(step.key, newPreviewMode);
|
||||||
this.renderStep();
|
|
||||||
|
// If switching to preview mode, render preview immediately
|
||||||
|
if (newPreviewMode) {
|
||||||
|
// Update preview container visibility
|
||||||
|
previewContainer.style.display = "block";
|
||||||
|
textEditorContainer.style.display = "none";
|
||||||
|
backToEditWrapper.style.display = "block";
|
||||||
|
// Render preview content (use existingValue as fallback)
|
||||||
|
const valueToRender = currentValue || existingValue || "";
|
||||||
|
await this.updatePreview(previewContainer, valueToRender);
|
||||||
|
} else {
|
||||||
|
// Switching back to edit mode
|
||||||
|
previewContainer.style.display = "none";
|
||||||
|
textEditorContainer.style.display = "block";
|
||||||
|
backToEditWrapper.style.display = "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
textEditorContainer.insertBefore(toolbar, textEditorContainer.firstChild);
|
textEditorContainer.insertBefore(toolbar, textEditorContainer.firstChild);
|
||||||
|
|
@ -388,7 +443,10 @@ export class InterviewWizardModal extends Modal {
|
||||||
|
|
||||||
// Render preview if in preview mode
|
// Render preview if in preview mode
|
||||||
if (isPreviewMode && existingValue) {
|
if (isPreviewMode && existingValue) {
|
||||||
this.updatePreview(previewContainer, existingValue);
|
this.updatePreview(previewContainer, existingValue).then(() => {
|
||||||
|
// After preview is rendered, ensure back button is visible
|
||||||
|
backToEditWrapper.style.display = "block";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,28 +584,91 @@ export class InterviewWizardModal extends Modal {
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
markdown: string
|
markdown: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
container.empty();
|
console.log("updatePreview called", {
|
||||||
if (!markdown.trim()) {
|
markdownLength: markdown?.length || 0,
|
||||||
container.createEl("p", {
|
markdownPreview: markdown?.substring(0, 100) || "(empty)",
|
||||||
|
containerExists: !!container,
|
||||||
|
containerId: container.id || "no-id",
|
||||||
|
containerClasses: container.className,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear container but keep structure
|
||||||
|
const existingChildren = Array.from(container.children);
|
||||||
|
for (const child of existingChildren) {
|
||||||
|
child.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!markdown || !markdown.trim()) {
|
||||||
|
const emptyEl = container.createEl("p", {
|
||||||
text: "(empty)",
|
text: "(empty)",
|
||||||
cls: "text-muted",
|
cls: "text-muted",
|
||||||
});
|
});
|
||||||
|
console.log("Preview: showing empty message");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Obsidian's MarkdownRenderer
|
// Use Obsidian's MarkdownRenderer
|
||||||
// Create a component for the renderer
|
// Create a component for the renderer
|
||||||
const component = new Component();
|
const component = new Component();
|
||||||
// Register it with the modal (Modal extends Component)
|
|
||||||
(this as any).addChild(component);
|
|
||||||
|
|
||||||
await MarkdownRenderer.render(
|
try {
|
||||||
this.app,
|
console.log("Calling MarkdownRenderer.render", {
|
||||||
markdown,
|
markdownLength: markdown.length,
|
||||||
container,
|
filePath: this.file.path,
|
||||||
this.file.path,
|
containerTag: container.tagName,
|
||||||
component
|
});
|
||||||
);
|
|
||||||
|
// IMPORTANT: Component must be loaded before rendering
|
||||||
|
// This ensures proper lifecycle management
|
||||||
|
component.load();
|
||||||
|
|
||||||
|
await MarkdownRenderer.render(
|
||||||
|
this.app,
|
||||||
|
markdown,
|
||||||
|
container,
|
||||||
|
this.file.path,
|
||||||
|
component
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Preview rendered successfully", {
|
||||||
|
containerChildren: container.children.length,
|
||||||
|
containerHTML: container.innerHTML.substring(0, 200),
|
||||||
|
});
|
||||||
|
|
||||||
|
// If container is still empty after rendering, try alternative approach
|
||||||
|
if (container.children.length === 0) {
|
||||||
|
console.warn("Container is empty after MarkdownRenderer.render, trying alternative");
|
||||||
|
// Fallback: create a simple div with the markdown as HTML (basic rendering)
|
||||||
|
const fallbackDiv = container.createEl("div", {
|
||||||
|
cls: "markdown-preview-fallback",
|
||||||
|
});
|
||||||
|
// Simple markdown to HTML conversion (very basic)
|
||||||
|
const html = markdown
|
||||||
|
.replace(/\n\n/g, "</p><p>")
|
||||||
|
.replace(/\n/g, "<br>")
|
||||||
|
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
|
||||||
|
.replace(/\*(.+?)\*/g, "<em>$1</em>")
|
||||||
|
.replace(/`(.+?)`/g, "<code>$1</code>");
|
||||||
|
fallbackDiv.innerHTML = `<p>${html}</p>`;
|
||||||
|
console.log("Fallback preview rendered");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up component when done (optional, but good practice)
|
||||||
|
// Component will be cleaned up when modal closes
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error rendering preview", error);
|
||||||
|
const errorEl = container.createEl("p", {
|
||||||
|
text: `Error rendering preview: ${String(error)}`,
|
||||||
|
cls: "text-error",
|
||||||
|
});
|
||||||
|
// Also show the raw markdown for debugging
|
||||||
|
const rawEl = container.createEl("pre", {
|
||||||
|
text: markdown,
|
||||||
|
cls: "text-muted",
|
||||||
|
});
|
||||||
|
rawEl.style.fontSize = "0.8em";
|
||||||
|
rawEl.style.overflow = "auto";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCaptureFrontmatterStep(
|
renderCaptureFrontmatterStep(
|
||||||
|
|
@ -1091,29 +1212,33 @@ export class InterviewWizardModal extends Modal {
|
||||||
previewContainer.style.overflowY = "auto";
|
previewContainer.style.overflowY = "auto";
|
||||||
previewContainer.style.position = "relative";
|
previewContainer.style.position = "relative";
|
||||||
|
|
||||||
// Add "Back to Edit" button in preview container
|
// Add "Back to Edit" button wrapper (outside preview content, so it doesn't get cleared)
|
||||||
if (isPreviewMode) {
|
const backToEditWrapper = editorContainer.createEl("div", {
|
||||||
const backToEditBtn = previewContainer.createEl("button", {
|
cls: "preview-back-button-wrapper",
|
||||||
text: "✏️ Zurück zum Bearbeiten",
|
});
|
||||||
cls: "mod-cta",
|
backToEditWrapper.style.display = isPreviewMode ? "block" : "none";
|
||||||
});
|
backToEditWrapper.style.position = "absolute";
|
||||||
backToEditBtn.style.position = "absolute";
|
backToEditWrapper.style.top = "0.5em";
|
||||||
backToEditBtn.style.top = "0.5em";
|
backToEditWrapper.style.right = "0.5em";
|
||||||
backToEditBtn.style.right = "0.5em";
|
backToEditWrapper.style.zIndex = "20";
|
||||||
backToEditBtn.style.zIndex = "10";
|
|
||||||
backToEditBtn.onclick = () => {
|
|
||||||
// Get current value from preview (it's already in draft)
|
|
||||||
const currentValue = existingValue;
|
|
||||||
// Update draft with current value
|
|
||||||
onFieldChange(nestedStep.key, currentValue);
|
|
||||||
|
|
||||||
// Toggle preview mode off
|
const backToEditBtn = backToEditWrapper.createEl("button", {
|
||||||
this.previewMode.set(previewKey, false);
|
text: "✏️ Zurück zum Bearbeiten",
|
||||||
|
cls: "mod-cta",
|
||||||
|
});
|
||||||
|
backToEditBtn.onclick = () => {
|
||||||
|
// Get current value from draft (it's already saved)
|
||||||
|
const currentLoopState = this.state.loopRuntimeStates.get(loopKey);
|
||||||
|
const currentValue = currentLoopState?.draft[nestedStep.key] || existingValue;
|
||||||
|
// Update draft with current value (ensure it's saved)
|
||||||
|
onFieldChange(nestedStep.key, String(currentValue));
|
||||||
|
|
||||||
// Re-render to show editor
|
// Toggle preview mode off
|
||||||
this.renderStep();
|
this.previewMode.set(previewKey, false);
|
||||||
};
|
|
||||||
}
|
// Re-render to show editor
|
||||||
|
this.renderStep();
|
||||||
|
};
|
||||||
|
|
||||||
// Editor container
|
// Editor container
|
||||||
const textEditorContainer = editorContainer.createEl("div", {
|
const textEditorContainer = editorContainer.createEl("div", {
|
||||||
|
|
@ -1158,18 +1283,66 @@ export class InterviewWizardModal extends Modal {
|
||||||
if (textarea) {
|
if (textarea) {
|
||||||
const itemToolbar = createMarkdownToolbar(
|
const itemToolbar = createMarkdownToolbar(
|
||||||
textarea,
|
textarea,
|
||||||
() => {
|
async () => {
|
||||||
// Get current value from textarea before toggling
|
// Get current value from textarea before toggling
|
||||||
const currentValue = textarea.value;
|
let currentValue = textarea.value;
|
||||||
|
console.log("Preview toggle clicked (loop)", {
|
||||||
|
textareaValue: currentValue,
|
||||||
|
textareaValueLength: currentValue?.length || 0,
|
||||||
|
existingValue: existingValue,
|
||||||
|
existingValueLength: existingValue?.length || 0,
|
||||||
|
previewKey: previewKey,
|
||||||
|
loopKey: loopKey,
|
||||||
|
nestedStepKey: nestedStep.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If textarea is empty, try to get from draft
|
||||||
|
if (!currentValue || currentValue.trim() === "") {
|
||||||
|
const currentLoopState = this.state.loopRuntimeStates.get(loopKey);
|
||||||
|
console.log("Textarea empty, checking draft", {
|
||||||
|
loopStateExists: !!currentLoopState,
|
||||||
|
draftValue: currentLoopState?.draft[nestedStep.key],
|
||||||
|
});
|
||||||
|
if (currentLoopState) {
|
||||||
|
const draftValue = currentLoopState.draft[nestedStep.key];
|
||||||
|
if (draftValue) {
|
||||||
|
currentValue = String(draftValue);
|
||||||
|
console.log("Using draft value", { draftValue: currentValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update draft with current value
|
// Update draft with current value
|
||||||
onFieldChange(nestedStep.key, currentValue);
|
onFieldChange(nestedStep.key, currentValue);
|
||||||
|
|
||||||
// Toggle preview mode
|
// Toggle preview mode
|
||||||
const newPreviewMode = !this.previewMode.get(previewKey);
|
const newPreviewMode = !this.previewMode.get(previewKey);
|
||||||
this.previewMode.set(previewKey, newPreviewMode);
|
this.previewMode.set(previewKey, newPreviewMode);
|
||||||
|
console.log("Preview mode toggled", {
|
||||||
|
newPreviewMode: newPreviewMode,
|
||||||
|
previewKey: previewKey,
|
||||||
|
});
|
||||||
|
|
||||||
// Re-render to show/hide preview
|
// If switching to preview mode, render preview immediately
|
||||||
this.renderStep();
|
if (newPreviewMode) {
|
||||||
|
// Update preview container visibility
|
||||||
|
previewContainer.style.display = "block";
|
||||||
|
textEditorContainer.style.display = "none";
|
||||||
|
backToEditWrapper.style.display = "block";
|
||||||
|
// Render preview content (use existingValue as fallback)
|
||||||
|
const valueToRender = currentValue || existingValue || "";
|
||||||
|
console.log("Rendering preview", {
|
||||||
|
valueToRender: valueToRender,
|
||||||
|
valueLength: valueToRender.length,
|
||||||
|
valuePreview: valueToRender.substring(0, 100),
|
||||||
|
});
|
||||||
|
await this.updatePreview(previewContainer, valueToRender);
|
||||||
|
} else {
|
||||||
|
// Switching back to edit mode
|
||||||
|
previewContainer.style.display = "none";
|
||||||
|
textEditorContainer.style.display = "block";
|
||||||
|
backToEditWrapper.style.display = "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
textEditorContainer.insertBefore(itemToolbar, textEditorContainer.firstChild);
|
textEditorContainer.insertBefore(itemToolbar, textEditorContainer.firstChild);
|
||||||
|
|
@ -1179,7 +1352,10 @@ export class InterviewWizardModal extends Modal {
|
||||||
|
|
||||||
// Render preview if in preview mode
|
// Render preview if in preview mode
|
||||||
if (isPreviewMode && existingValue) {
|
if (isPreviewMode && existingValue) {
|
||||||
this.updatePreview(previewContainer, existingValue);
|
this.updatePreview(previewContainer, existingValue).then(() => {
|
||||||
|
// After preview is rendered, ensure back button is visible
|
||||||
|
backToEditWrapper.style.display = "block";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (nestedStep.type === "capture_text_line") {
|
} else if (nestedStep.type === "capture_text_line") {
|
||||||
// Field container
|
// Field container
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user