refactoring of cmaps, widgets, etc.

This commit is contained in:
2026-09-02 16:06:26 +02:00
parent 38f255c1a4
commit f0562a06cc
52 changed files with 3626 additions and 2642 deletions
@@ -0,0 +1,107 @@
import { StatusField } from "../../../widgets/status-field.js";
/**
* Run the user-facing Markdown and JSON export actions for one CMap.
*
* Export generation is supplied by the workspace. This controller owns export
* options, progress messages, clipboard handling and browser downloads.
*/
export class CmapExportDialog {
constructor(dialog, tr, buildMarkdown, buildJson) {
this.dialog = dialog;
this.tr = tr;
this.buildMarkdown = buildMarkdown;
this.buildJson = buildJson;
this.form = dialog.querySelector("form");
this.depth = dialog.querySelector("#cmap-export-depth");
this.pages = dialog.querySelector("#cmap-export-pages");
this.status = new StatusField(dialog.querySelector("#cmap-export-status"));
this.conceptMapSlug = "concept-map";
dialog.querySelector("#cmap-export-cancel").addEventListener("click", () => dialog.close());
dialog.querySelector("#cmap-export-copy").addEventListener("click", () => {
this.exportMarkdown(false);
});
dialog.querySelector("#cmap-export-json").addEventListener("click", () => {
this.exportJson();
});
this.form.addEventListener("submit", (event) => {
event.preventDefault();
this.exportMarkdown(true);
});
}
open(conceptMapSlug) {
this.conceptMapSlug = conceptMapSlug;
this.status.clear();
this.dialog.showModal();
this.depth.focus();
}
options() {
return {
depth: Math.max(0, Math.min(10, Number(this.depth.value) || 0)),
includeWikiPages: this.pages.checked
};
}
async exportJson() {
this.status.set(this.tr("preparing-cmap-json", "Preparing complete CMap JSON…"));
try {
const bundle = await this.buildJson(this.options().depth);
this.download(`${JSON.stringify(bundle, null, 2)}\n`,
`${bundle.rootCmapSlug}-cmap.json`, "application/json;charset=utf-8");
this.status.set(this.tr("cmap-json-downloaded", "CMap JSON downloaded."));
return true;
} catch (error) {
this.status.set(error.message);
return false;
}
}
async exportMarkdown(download) {
this.status.set(this.tr("preparing-markdown-export", "Preparing Markdown export…"));
try {
const markdown = await this.buildMarkdown(this.options());
if (download) {
this.download(markdown, `${this.conceptMapSlug}-report.md`,
"text/markdown;charset=utf-8");
this.status.set(this.tr("markdown-export-downloaded", "Markdown export downloaded."));
} else {
await this.copy(markdown);
this.status.set(this.tr("markdown-export-copied", "Markdown export copied."));
}
return true;
} catch (error) {
this.status.set(error.message);
return false;
}
}
download(text, filename, contentType) {
const blob = new Blob([text], { type: contentType });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
document.body.append(link);
link.click();
link.remove();
window.setTimeout(() => URL.revokeObjectURL(url), 0);
}
async copy(text) {
try {
await navigator.clipboard.writeText(text);
} catch (_error) {
const input = document.createElement("textarea");
input.value = text;
input.style.position = "fixed";
input.style.left = "-10000px";
document.body.append(input);
input.select();
document.execCommand("copy");
input.remove();
}
}
}