Top-bottom panel refactor, more instant rendering

This commit is contained in:
Zef Hemel
2023-12-27 18:05:47 +01:00
parent 9403fd2cd9
commit 4d66f23391
16 changed files with 227 additions and 327 deletions
+1 -1
View File
@@ -70,10 +70,10 @@ export function fencedCodePlugin(editor: Client) {
const widget = renderMode === "markdown"
? new MarkdownWidget(
from + lineStrings[0].length + 1,
to - lineStrings[lineStrings.length - 1].length - 1,
editor,
lineStrings.slice(1, lineStrings.length - 1).join("\n"),
codeWidgetCallback,
"sb-markdown-widget",
)
: new IFrameWidget(
from + lineStrings[0].length + 1,
+45 -17
View File
@@ -11,21 +11,25 @@ export class MarkdownWidget extends WidgetType {
renderedMarkdown?: string;
constructor(
readonly from: number,
readonly to: number,
readonly from: number | undefined,
readonly client: Client,
readonly bodyText: string,
readonly codeWidgetCallback: CodeWidgetCallback,
readonly className: string,
) {
super();
}
toDOM(): HTMLElement {
const div = document.createElement("div");
div.className = "sb-markdown-widget";
div.className = this.className;
const cacheItem = this.client.getWidgetCache(this.bodyText);
if (cacheItem) {
div.innerHTML = this.wrapHtml(cacheItem.html);
div.innerHTML = this.wrapHtml(
cacheItem.html,
this.from !== undefined,
this.from !== undefined,
);
this.attachListeners(div);
}
@@ -43,6 +47,11 @@ export class MarkdownWidget extends WidgetType {
this.bodyText,
this.client.currentPage!,
);
if (!widgetContent) {
div.innerHTML = "";
// div.style.display = "none";
return;
}
const lang = buildMarkdown(this.client.system.mdExtensions);
let mdTree = parse(
lang,
@@ -80,7 +89,11 @@ export class MarkdownWidget extends WidgetType {
// HTML still same as in cache, no need to re-render
return;
}
div.innerHTML = this.wrapHtml(html);
div.innerHTML = this.wrapHtml(
html,
this.from !== undefined,
this.from !== undefined,
);
this.attachListeners(div);
// Let's give it a tick, then measure and cache
@@ -93,12 +106,20 @@ export class MarkdownWidget extends WidgetType {
});
}
private wrapHtml(html: string) {
private wrapHtml(html: string, editButton = true, sourceButton = true) {
return `
<div class="button-bar">
<button class="source-button" title="Show Markdown source"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-code"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg></button>
${
sourceButton
? `<button class="source-button" title="Show Markdown source"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-code"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg></button>`
: ""
}
<button class="reload-button" title="Reload"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg></button>
<button class="edit-button" title="Edit"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg></button>
${
editButton
? `<button class="edit-button" title="Edit"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg></button>`
: ""
}
</div>
${html}`;
}
@@ -109,7 +130,12 @@ export class MarkdownWidget extends WidgetType {
// Override default click behavior with a local navigate (faster)
el.addEventListener("click", (e) => {
e.preventDefault();
this.client.navigate(el.dataset.ref!);
const [pageName, pos] = el.dataset.ref!.split(/[$@]/);
if (pos && pos.match(/^\d+$/)) {
this.client.navigate(pageName, +pos);
} else {
this.client.navigate(pageName, pos);
}
});
});
@@ -134,18 +160,20 @@ export class MarkdownWidget extends WidgetType {
);
});
div.querySelector(".edit-button")!.addEventListener("click", () => {
this.client.editorView.dispatch({
selection: { anchor: this.from },
if (this.from !== undefined) {
div.querySelector(".edit-button")!.addEventListener("click", () => {
this.client.editorView.dispatch({
selection: { anchor: this.from! },
});
this.client.focus();
});
this.client.focus();
});
div.querySelector(".source-button")!.addEventListener("click", () => {
div.innerText = this.renderedMarkdown!;
});
}
div.querySelector(".reload-button")!.addEventListener("click", () => {
this.renderContent(div, undefined).catch(console.error);
});
div.querySelector(".source-button")!.addEventListener("click", () => {
div.innerText = this.renderedMarkdown!;
});
}
get estimatedHeight(): number {
+19 -46
View File
@@ -1,67 +1,40 @@
import { Decoration, EditorState, WidgetType } from "../deps.ts";
import type { Client } from "../client.ts";
import { decoratorStateField } from "./util.ts";
import { PanelConfig } from "../types.ts";
import { createWidgetSandboxIFrame } from "../components/widget_sandbox_iframe.ts";
import { MarkdownWidget } from "./markdown_widget.ts";
class IFrameWidget extends WidgetType {
widgetHeightCacheKey: string;
constructor(
readonly editor: Client,
readonly panel: PanelConfig,
readonly className: string,
) {
super();
this.widgetHeightCacheKey = `${this.editor.currentPage!}#${this.className}`;
}
toDOM(): HTMLElement {
const iframe = createWidgetSandboxIFrame(
this.editor,
this.widgetHeightCacheKey,
this.panel,
);
iframe.classList.add(this.className);
return iframe;
}
get estimatedHeight(): number {
return this.editor.space.getCachedWidgetHeight(
this.widgetHeightCacheKey,
);
}
eq(other: WidgetType): boolean {
return this.panel.html ===
(other as IFrameWidget).panel.html &&
this.panel.script ===
(other as IFrameWidget).panel.script;
}
}
export function postScriptPrefacePlugin(editor: Client) {
export function postScriptPrefacePlugin(
editor: Client,
) {
const panelWidgetHook = editor.system.panelWidgetHook;
return decoratorStateField((state: EditorState) => {
const widgets: any[] = [];
if (editor.ui.viewState.panels.top.html) {
const topCallback = panelWidgetHook.callbacks.get("top");
if (topCallback) {
widgets.push(
Decoration.widget({
widget: new IFrameWidget(
widget: new MarkdownWidget(
undefined,
editor,
editor.ui.viewState.panels.top,
"sb-top-iframe",
`top:${editor.currentPage}`,
topCallback,
"sb-markdown-top-widget",
),
side: -1,
block: true,
}).range(0),
);
}
if (editor.ui.viewState.panels.bottom.html) {
const bottomCallback = panelWidgetHook.callbacks.get("bottom");
if (bottomCallback) {
widgets.push(
Decoration.widget({
widget: new IFrameWidget(
widget: new MarkdownWidget(
undefined,
editor,
editor.ui.viewState.panels.bottom,
"sb-bottom-iframe",
`bottom:${editor.currentPage}`,
bottomCallback,
"sb-markdown-bottom-widget",
),
side: 1,
block: true,