Selaa lähdekoodia

:recycle: outline

Liyuan Li 5 vuotta sitten
vanhempi
sitoutus
8acf2e827f

+ 7 - 14
src/index.ts

@@ -2,12 +2,14 @@ import VditorMethod from "./method";
 import {VDITOR_VERSION} from "./ts/constants";
 import {DevTools} from "./ts/devtools";
 import {Hint} from "./ts/hint/index";
+import {i18n} from "./ts/i18n";
 import {IR} from "./ts/ir";
 import {input as irInput} from "./ts/ir/input";
 import {processAfterRender} from "./ts/ir/process";
 import {getHTML} from "./ts/markdown/getHTML";
 import {getMarkdown} from "./ts/markdown/getMarkdown";
 import {setLute} from "./ts/markdown/setLute";
+import {Outline} from "./ts/outline";
 import {Preview} from "./ts/preview/index";
 import {Resize} from "./ts/resize/index";
 import {formatRender} from "./ts/sv/formatRender";
@@ -29,7 +31,6 @@ import {IRUndo} from "./ts/undo/IRUndo";
 import {WysiwygUndo} from "./ts/undo/WysiwygUndo";
 import {Upload} from "./ts/upload/index";
 import {addScript} from "./ts/util/addScript";
-import {renderOutline} from "./ts/util/fixBrowserBehavior";
 import {Options} from "./ts/util/Options";
 import {getCursorPosition, getEditorRange, setSelectionByPosition} from "./ts/util/selection";
 import {WYSIWYG} from "./ts/wysiwyg";
@@ -74,6 +75,7 @@ class Vditor extends VditorMethod {
             lute: undefined,
             options: mergedOptions,
             originalInnerHTML: id.innerHTML,
+            outline: new Outline(i18n[mergedOptions.lang].outline),
             tip: new Tip(),
         };
 
@@ -83,15 +85,12 @@ class Vditor extends VditorMethod {
         this.vditor.wysiwygUndo = new WysiwygUndo();
         this.vditor.irUndo = new IRUndo();
         this.vditor.ir = new IR(this.vditor);
+        this.vditor.toolbar = new Toolbar(this.vditor);
 
         if (mergedOptions.resize.enable) {
             this.vditor.resize = new Resize(this.vditor);
         }
 
-        if (mergedOptions.toolbar) {
-            this.vditor.toolbar = new Toolbar(this.vditor);
-        }
-
         if (this.vditor.toolbar.elements.devtools) {
             this.vditor.devtools = new DevTools();
         }
@@ -121,9 +120,7 @@ class Vditor extends VditorMethod {
                 toc: this.vditor.options.preview.markdown.toc,
             });
 
-            if (this.vditor.toolbar.elements.preview || this.vditor.toolbar.elements.both) {
-                this.vditor.preview = new Preview(this.vditor);
-            }
+            this.vditor.preview = new Preview(this.vditor);
 
             initUI(this.vditor);
 
@@ -222,9 +219,7 @@ class Vditor extends VditorMethod {
 
     /** 设置预览区域内容 */
     public renderPreview(value?: string) {
-        if (this.vditor.currentMode === "sv") {
-            this.vditor.preview.render(this.vditor, value);
-        }
+        this.vditor.preview.render(this.vditor, value);
     }
 
     /** 获取焦点位置 */
@@ -348,9 +343,7 @@ class Vditor extends VditorMethod {
             });
         }
 
-        if (this.vditor.toolbar.elements.outline && this.vditor.options.outline && this.vditor.currentMode !== "sv") {
-            renderOutline(this.vditor);
-        }
+        this.vditor.outline.render(this.vditor);
 
         if (!markdown) {
             hidePanel(this.vditor, ["emoji", "headings", "submenu", "hint"]);

+ 1 - 5
src/ts/hint/index.ts

@@ -84,11 +84,7 @@ export class Hint {
 
         const editorElement = vditor[vditor.currentMode].element;
         const textareaPosition = getCursorPosition(editorElement);
-        let x = textareaPosition.left;
-        const outlineElement = vditor.element.querySelector(".vditor-outline") as HTMLElement;
-        if (outlineElement) {
-            x = x + outlineElement.offsetWidth;
-        }
+        const x = textareaPosition.left + vditor.outline.element.offsetWidth;
         const y = textareaPosition.top;
         let hintsHTML = "";
 

+ 37 - 0
src/ts/outline/index.ts

@@ -0,0 +1,37 @@
+import {outlineRender} from "../markdown/outlineRender";
+import {setPadding} from "../ui/initUI";
+
+export class Outline {
+    public element: HTMLElement;
+
+    constructor(outlineLabel: string) {
+        this.element = document.createElement("div");
+        this.element.className = "vditor-outline";
+        this.element.innerHTML = `<div class="vditor-outline__title">${outlineLabel}</div>
+<div class="vditor-outline__content"></div>`;
+    }
+
+    public render(vditor: IVditor) {
+        if (this.element.style.display === "block") {
+            if (vditor.preview.element.style.display === "block") {
+                outlineRender(vditor.preview.element.lastElementChild as HTMLElement,
+                    this.element.lastElementChild, vditor);
+            } else {
+                outlineRender(vditor[vditor.currentMode].element, this.element.lastElementChild, vditor);
+            }
+        }
+    }
+
+    public toggle(vditor: IVditor, show = true) {
+        const btnElement = vditor.toolbar.elements.outline?.firstElementChild;
+        if (show) {
+            this.element.style.display = "block";
+            this.render(vditor);
+            btnElement?.classList.add("vditor-menu--current");
+        } else {
+            this.element.style.display = "none";
+            btnElement?.classList.remove("vditor-menu--current");
+        }
+        setPadding(vditor);
+    }
+}

+ 5 - 4
src/ts/toolbar/EditMode.ts

@@ -10,7 +10,6 @@ import {processCodeRender} from "../util/processCode";
 import {highlightToolbar} from "../wysiwyg/highlightToolbar";
 import {renderDomByMd} from "../wysiwyg/renderDomByMd";
 import {MenuItem} from "./MenuItem";
-import {toggleOutline} from "./Outline";
 import {
     disableToolbar,
     enableToolbar,
@@ -114,6 +113,9 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
         }
         setPadding(vditor);
     }
+    if (typeof event === "string") {
+        vditor.outline.render(vditor);
+    }
     setTypewriterPosition(vditor);
 
     if (vditor.toolbar.elements["edit-mode"]) {
@@ -122,9 +124,8 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
         });
         vditor.toolbar.elements["edit-mode"].querySelector(`button[data-mode="${vditor.currentMode}"]`).classList.add("vditor-menu--current");
     }
-    if (vditor.toolbar.elements.outline) {
-        toggleOutline(vditor, vditor.currentMode !== "sv" && vditor.options.outline);
-    }
+
+    vditor.outline.toggle(vditor, vditor.currentMode !== "sv" && vditor.options.outline);
 };
 
 export class EditMode extends MenuItem {

+ 5 - 21
src/ts/toolbar/Outline.ts

@@ -1,27 +1,7 @@
 import {Constants} from "../constants";
-import {setPadding} from "../ui/initUI";
 import {getEventName} from "../util/compatibility";
-import {renderOutline} from "../util/fixBrowserBehavior";
 import {MenuItem} from "./MenuItem";
 
-export const toggleOutline = (vditor: IVditor, show = true) => {
-    const btnElement = vditor.toolbar.elements.outline.firstElementChild;
-    if (btnElement.classList.contains(Constants.CLASS_MENU_DISABLED)) {
-        return;
-    }
-
-    const outlineElement = vditor.element.querySelector(".vditor-outline") as HTMLElement;
-    if (show) {
-        outlineElement.style.display = "block";
-        renderOutline(vditor);
-        btnElement.classList.add("vditor-menu--current");
-    } else {
-        outlineElement.style.display = "none";
-        btnElement.classList.remove("vditor-menu--current");
-    }
-    setPadding(vditor);
-};
-
 export class Outline extends MenuItem {
     constructor(vditor: IVditor, menuItem: IMenuItem) {
         super(vditor, menuItem);
@@ -30,8 +10,12 @@ export class Outline extends MenuItem {
         }
         this.element.children[0].addEventListener(getEventName(), (event) => {
             event.preventDefault();
+            const btnElement = vditor.toolbar.elements.outline.firstElementChild;
+            if (btnElement.classList.contains(Constants.CLASS_MENU_DISABLED)) {
+                return;
+            }
             vditor.options.outline = !this.element.firstElementChild.classList.contains("vditor-menu--current");
-            toggleOutline(vditor, vditor.options.outline);
+            vditor.outline.toggle(vditor, vditor.options.outline);
         });
     }
 }

+ 2 - 3
src/ts/toolbar/Preview.ts

@@ -1,7 +1,6 @@
 import {Constants} from "../constants";
 import {setPadding} from "../ui/initUI";
 import {getEventName} from "../util/compatibility";
-import {renderOutline} from "../util/fixBrowserBehavior";
 import {MenuItem} from "./MenuItem";
 import {disableToolbar, enableToolbar, hidePanel} from "./setToolbar";
 
@@ -34,7 +33,7 @@ export class Preview extends MenuItem {
                     vditor.preview.element.style.display = "none";
                 }
                 enableToolbar(vditor.toolbar.elements, toolbars);
-                renderOutline(vditor);
+                vditor.outline.render(vditor);
             } else {
                 disableToolbar(vditor.toolbar.elements, toolbars);
                 vditor.preview.element.style.display = "block";
@@ -47,7 +46,7 @@ export class Preview extends MenuItem {
                 btnElement.classList.add("vditor-menu--current");
                 hidePanel(vditor, ["subToolbar", "hint", "popover"]);
                 setTimeout(() => {
-                    renderOutline(vditor);
+                    vditor.outline.render(vditor);
                 }, vditor.options.preview.delay + 10);
             }
             setPadding(vditor);

+ 3 - 9
src/ts/ui/initUI.ts

@@ -1,4 +1,3 @@
-import {i18n} from "../i18n";
 import {html2md} from "../sv/html2md";
 import {setEditMode} from "../toolbar/EditMode";
 import {setContentTheme} from "./setContentTheme";
@@ -32,13 +31,7 @@ export const initUI = (vditor: IVditor) => {
     const contentElement = document.createElement("div");
     contentElement.className = "vditor-content";
 
-    if (vditor.toolbar.elements.outline) {
-        const outlineElement = document.createElement("div");
-        outlineElement.className = "vditor-outline";
-        outlineElement.innerHTML = `<div class="vditor-outline__title">${i18n[vditor.options.lang].outline}</div>
-<div class="vditor-outline__content"></div>`;
-        contentElement.appendChild(outlineElement);
-    }
+    contentElement.appendChild(vditor.outline.element);
 
     contentElement.appendChild(vditor.wysiwyg.element.parentElement);
 
@@ -113,7 +106,8 @@ export const setPadding = (vditor: IVditor) => {
 
     if (vditor.preview.element.style.display !== "block" || vditor.currentMode === "sv") {
         vditor.toolbar.element.style.paddingLeft = Math.max(5,
-            parseInt(vditor[vditor.currentMode].element.style.paddingLeft || "0", 10) + 250) + "px";
+            parseInt(vditor[vditor.currentMode].element.style.paddingLeft || "0", 10) +
+            vditor.outline.element.offsetWidth) + "px";
     }
 };
 

+ 3 - 18
src/ts/util/fixBrowserBehavior.ts

@@ -1,7 +1,6 @@
 import {Constants} from "../constants";
 import {highlightToolbar as highlightToolbarIR} from "../ir/highlightToolbar";
 import {processAfterRender} from "../ir/process";
-import {outlineRender} from "../markdown/outlineRender";
 import {uploadFiles} from "../upload";
 import {setHeaders} from "../upload/setHeaders";
 import {processCodeRender, processPasteCode} from "../util/processCode";
@@ -384,23 +383,9 @@ export const isToC = (text: string) => {
     return text.trim().toLowerCase() === "[toc]";
 };
 
-export const renderOutline = (vditor: IVditor) => {
-    const outlineElement = vditor.element.querySelector(".vditor-outline") as HTMLElement;
-    if (outlineElement && outlineElement.style.display === "block") {
-        const previewElement: HTMLElement = vditor.element.querySelector(".vditor-preview");
-        if (previewElement && previewElement.style.display === "block") {
-            outlineRender(previewElement.lastElementChild as HTMLElement,
-                outlineElement.querySelector(".vditor-outline__content"), vditor);
-        } else {
-            outlineRender(vditor[vditor.currentMode].element,
-                outlineElement.querySelector(".vditor-outline__content"), vditor);
-        }
-    }
-};
-
 export const renderToc = (vditor: IVditor) => {
     const editorElement = vditor[vditor.currentMode].element;
-    renderOutline(vditor);
+    vditor.outline.render(vditor);
     const tocElement = editorElement.querySelector('[data-type="toc-block"]');
     if (!tocElement) {
         return;
@@ -1207,7 +1192,7 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
                 vditor.lute.SetJSRenderers({renderers});
                 insertHTML(vditor.lute.HTML2VditorDOM(tempElement.innerHTML), vditor);
             }
-            renderOutline(vditor);
+            vditor.outline.render(vditor);
         } else if (event.clipboardData.files.length > 0 && vditor.options.upload.url) {
             uploadFiles(vditor, event.clipboardData.files);
         } else if (textPlain.trim() !== "" && event.clipboardData.files.length === 0) {
@@ -1220,7 +1205,7 @@ export const paste = (vditor: IVditor, event: ClipboardEvent & { target: HTMLEle
                 vditor.lute.SetJSRenderers({renderers});
                 insertHTML(vditor.lute.Md2VditorDOM(textPlain), vditor);
             }
-            renderOutline(vditor);
+            vditor.outline.render(vditor);
         }
     }
     vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)

+ 5 - 0
types/index.d.ts

@@ -499,6 +499,11 @@ interface IVditor {
         element: HTMLDivElement,
         renderEchart(vditor: IVditor): void,
     };
+    outline: {
+        element: HTMLElement,
+        render(vditor: IVditor): void,
+        toggle(vditor: IVditor, show?: boolean): void,
+    };
     toolbar?: {
         elements?: { [key: string]: HTMLElement },
         element?: HTMLElement,