]> BookStack Code Mirror - bookstack/commitdiff
Updated markdown preview to update on diff-basis
authorDan Brown <redacted>
Tue, 7 Jun 2022 15:07:28 +0000 (16:07 +0100)
committerDan Brown <redacted>
Tue, 7 Jun 2022 15:07:28 +0000 (16:07 +0100)
Uses vdom system to diff and update the current markdown preview view
instead of requiring a full HTML replace change.
This should provide better performance, expecially where dynamically
loaded content such as iframes were in use.

Closes #3454

package-lock.json
package.json
resources/js/components/markdown-editor.js
resources/js/services/vdom.js [new file with mode: 0644]

index eea8b3cfa4356eb8d68cc784ec2f0b875f7e5f78..1448d592fa0f72b3f40b39fc4b86d033fb402928 100644 (file)
@@ -10,6 +10,7 @@
         "dropzone": "^5.9.3",
         "markdown-it": "^13.0.1",
         "markdown-it-task-lists": "^2.1.1",
+        "snabbdom": "^3.5.0",
         "sortablejs": "^1.15.0"
       },
       "devDependencies": {
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/snabbdom": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.0.tgz",
+      "integrity": "sha512-Ff5BKG18KrrPuskHJlA9aujPHqEabItaDl96l7ZZndF4zt5AYSczz7ZjjgQAX5IBd5cd25lw9NfgX21yVUJ+9g==",
+      "engines": {
+        "node": ">=8.3.0"
+      }
+    },
     "node_modules/sortablejs": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
         "object-inspect": "^1.9.0"
       }
     },
+    "snabbdom": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.0.tgz",
+      "integrity": "sha512-Ff5BKG18KrrPuskHJlA9aujPHqEabItaDl96l7ZZndF4zt5AYSczz7ZjjgQAX5IBd5cd25lw9NfgX21yVUJ+9g=="
+    },
     "sortablejs": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz",
index 539e0505726018fb3700cdbc3a1bc5a37ac60af8..9a2f664482a08958a2ae1d3e72fcce593e40c6bd 100644 (file)
@@ -28,6 +28,7 @@
     "dropzone": "^5.9.3",
     "markdown-it": "^13.0.1",
     "markdown-it-task-lists": "^2.1.1",
+    "snabbdom": "^3.5.0",
     "sortablejs": "^1.15.0"
   }
 }
index 297d9c8ece8bcdae60befb118b59ffc2388165de..21cf37bb4438d745a3dbbf7225773cf8474a6c83 100644 (file)
@@ -2,7 +2,7 @@ import MarkdownIt from "markdown-it";
 import mdTasksLists from 'markdown-it-task-lists';
 import Clipboard from "../services/clipboard";
 import {debounce} from "../services/util";
-
+import {patchDomFromHtmlString} from "../services/vdom";
 import DrawIO from "../services/drawio";
 
 class MarkdownEditor {
@@ -127,18 +127,31 @@ class MarkdownEditor {
     updateAndRender() {
         const content = this.cm.getValue();
         this.input.value = content;
+
         const html = this.markdown.render(content);
         window.$events.emit('editor-html-change', html);
         window.$events.emit('editor-markdown-change', content);
 
         // Set body content
+        const target = this.getDisplayTarget();
         this.displayDoc.body.className = 'page-content';
-        this.displayDoc.body.innerHTML = html;
+        patchDomFromHtmlString(target, html);
 
         // Copy styles from page head and set custom styles for editor
         this.loadStylesIntoDisplay();
     }
 
+    getDisplayTarget() {
+        const body = this.displayDoc.body;
+
+        if (body.children.length === 0) {
+            const wrap = document.createElement('div');
+            this.displayDoc.body.append(wrap);
+        }
+
+        return body.children[0];
+    }
+
     loadStylesIntoDisplay() {
         if (this.displayStylesLoaded) return;
         this.displayDoc.documentElement.classList.add('markdown-editor-display');
diff --git a/resources/js/services/vdom.js b/resources/js/services/vdom.js
new file mode 100644 (file)
index 0000000..89a8090
--- /dev/null
@@ -0,0 +1,31 @@
+import {
+    init,
+    attributesModule,
+    toVNode
+} from "snabbdom";
+
+let patcher;
+
+/**
+ * @returns {Function}
+ */
+function getPatcher() {
+    if (patcher) return patcher;
+
+
+    patcher = init([
+        attributesModule,
+    ]);
+
+    return patcher;
+}
+
+/**
+ * @param {Element} domTarget
+ * @param {String} html
+ */
+export function patchDomFromHtmlString(domTarget, html) {
+    const contentDom = document.createElement('div');
+    contentDom.innerHTML = html;
+    getPatcher()(toVNode(domTarget), toVNode(contentDom));
+}
\ No newline at end of file