Liyuan Li 5 yıl önce
ebeveyn
işleme
18d700edbb
6 değiştirilmiş dosya ile 102 ekleme ve 34 silme
  1. 7 3
      demo/comment.html
  2. 72 25
      demo/comment.js
  3. 1 1
      src/index.ts
  4. 0 0
      src/js/lute/lute.min.js
  5. 14 3
      src/ts/wysiwyg/index.ts
  6. 8 2
      types/index.d.ts

+ 7 - 3
demo/comment.html

@@ -51,7 +51,11 @@
         }
 
         #vditor {
-            margin: 0 20px 20px;
+            margin: 0 20px 0 20px;
+        }
+
+        #comments > div {
+            position: absolute;
         }
     </style>
     <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vconsole.min.js"></script>
@@ -67,9 +71,9 @@
         Light Mode
     </button>
 </div>
-<div style="display: flex">
+<div style="display: flex;height: 360px">
     <div id="vditor" style="flex: 1"></div>
-    <div id="comments" style="width: 300px"></div>
+    <div id="comments" style="width: 300px;overflow: auto;margin-top: 36px;position: relative"></div>
 </div>
 </body>
 </html>

+ 72 - 25
demo/comment.js

@@ -51,17 +51,18 @@ const renderComments = (ids) => {
     cmts = JSON.parse(cmts)
   }
 
-  ids.forEach(id => {
+  ids.forEach(commentData => {
     let text = ''
     cmts.find((item) => {
-      if (item.id === id) {
+      if (item.id === commentData.id) {
         text = item.text
         return true
       }
     })
 
     const cmtElement = document.createElement('div')
-    cmtElement.setAttribute('data-id', id)
+    cmtElement.setAttribute('data-id', commentData.id)
+    cmtElement.setAttribute('style', `top:${commentData.top}px`)
     cmtElement.innerHTML = `<div>
 ${text}<br>
 <button>删除</button><br>
@@ -70,49 +71,95 @@ ${text}<br>
     cmtElement.value = text
     document.getElementById('comments').
       insertAdjacentElement('beforeend', cmtElement)
+    // 高度调整
+    const previousElement = cmtElement.previousElementSibling
+    if (previousElement) {
+      const previousTop = parseInt(previousElement.style.top) +
+        previousElement.clientHeight + 20
+      if (previousTop > commentData.top) {
+        cmtElement.style.top = previousTop + 'px'
+      }
+    }
     bindCommentEvent(cmtElement)
   })
+
+  document.getElementById('comments').addEventListener("scroll", () => {
+    window.vditor.vditor.wysiwyg.element.scrollTop = document.getElementById('comments').scrollTop
+  })
+}
+
+const matchCommentsTop = (commentsData) => {
+  commentsData.forEach((item) => {
+    const commentElement = document.querySelector(
+      `#comments div[data-id="${item.id}"]`)
+    commentElement.setAttribute('style', `top:${item.top}px`)
+    // 高度调整
+    const previousElement = commentElement.previousElementSibling
+    if (previousElement) {
+      const previousTop = parseInt(previousElement.style.top) +
+        previousElement.clientHeight + 20
+      if (previousTop > item.top) {
+        commentElement.style.top = previousTop + 'px'
+      }
+    }
+  })
+
+  document.getElementById('comments').scrollTop = window.vditor.vditor.wysiwyg.element.scrollTop
 }
 
 window.vditor = new Vditor('vditor', {
-  // _lutePath: `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`,
-  _lutePath: 'src/js/lute/lute.min.js',
+  _lutePath: `http://192.168.0.107:9090/lute.min.js?${new Date().getTime()}`,
+  // _lutePath: 'src/js/lute/lute.min.js',
   mode: 'wysiwyg',
-  height: window.innerHeight + 100,
-  outline: true,
   debugger: true,
   typewriterMode: true,
   placeholder: 'Hello, Vditor!',
   comment: {
     enable: true,
-    add (id, text) {
-      const cmtElement = document.createElement('div')
-      cmtElement.setAttribute('data-id', id)
-      cmtElement.innerHTML = `<div>
+    add (id, text, commentsData) {
+      commentsData.find((item, index) => {
+        if (item.id === id) {
+          const cmtElement = document.createElement('div')
+          cmtElement.setAttribute('data-id', id)
+          cmtElement.innerHTML = `<div>
 ${text}<br>
 <button>删除</button><br>
 <input> 
 </div>`
-      cmtElement.value = text
-      document.getElementById('comments').
-        insertAdjacentElement('beforeend', cmtElement)
-      bindCommentEvent(cmtElement)
-      cmtElement.querySelector('input').focus()
-      let cmts = localStorage.getItem('cmts')
-      if (!cmts) {
-        localStorage.setItem('cmts', '[]')
-        cmts = []
-      } else {
-        cmts = JSON.parse(cmts)
-      }
-      cmts.push({id, text})
-      localStorage.setItem('cmts', JSON.stringify(cmts))
+          cmtElement.value = text
+          if (index === 0) {
+            document.querySelector('#comments').
+              insertAdjacentElement('beforeend', cmtElement)
+          } else {
+            document.querySelector(
+              `#comments div[data-id="${commentsData[index - 1].id}"]`).
+              insertAdjacentElement('afterend', cmtElement)
+          }
+          bindCommentEvent(cmtElement)
+          cmtElement.querySelector('input').focus()
+          let cmts = localStorage.getItem('cmts')
+          if (!cmts) {
+            localStorage.setItem('cmts', '[]')
+            cmts = []
+          } else {
+            cmts = JSON.parse(cmts)
+          }
+          cmts.push({id, text})
+          localStorage.setItem('cmts', JSON.stringify(cmts))
+          return true
+        }
+      })
+      matchCommentsTop(commentsData)
     },
     remove (ids) {
       ids.forEach((id) => {
         removeComment(document.querySelector(`#comments div[data-id="${id}"]`),
           id)
       })
+      matchCommentsTop(window.vditor.getCommentIds())
+    },
+    scroll (top) {
+      document.getElementById('comments').scrollTop = top
     },
   },
   after () {

+ 1 - 1
src/index.ts

@@ -364,7 +364,7 @@ class Vditor extends VditorMethod {
         if (this.vditor.currentMode !== "wysiwyg") {
             return [];
         }
-        return this.vditor.wysiwyg.getComments(this.vditor);
+        return this.vditor.wysiwyg.getComments(this.vditor, true);
     }
 
     /** 高亮评论 */

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/js/lute/lute.min.js


+ 14 - 3
src/ts/wysiwyg/index.ts

@@ -70,7 +70,6 @@ class WYSIWYG {
             this.selectPopover.querySelector("button").onclick = () => {
                 const id = Lute.NewNodeID();
                 const range = getSelection().getRangeAt(0);
-                vditor.options.comment.add(id, range.toString());
                 const contents = range.cloneContents();
                 range.deleteContents();
                 contents.childNodes.forEach((item: HTMLElement) => {
@@ -85,6 +84,7 @@ class WYSIWYG {
                     }
                 });
                 range.insertNode(contents);
+                vditor.options.comment.add(id, range.toString(), this.getComments(vditor, true));
                 afterRenderEvent(vditor, {
                     enableAddUndoStack: true,
                     enableHint: false,
@@ -95,7 +95,7 @@ class WYSIWYG {
         }
     }
 
-    public getComments(vditor: IVditor) {
+    public getComments(vditor: IVditor, getData = false) {
         if (vditor.currentMode === "wysiwyg" && vditor.options.comment.enable) {
             this.commentIds = [];
             this.element.querySelectorAll(".vditor-comment").forEach((item) => {
@@ -103,7 +103,17 @@ class WYSIWYG {
                     this.commentIds.concat(item.getAttribute("data-cmtids").split(" "));
             });
             this.commentIds = Array.from(new Set(this.commentIds));
-            return this.commentIds;
+
+            const comments: ICommentsData[] = [];
+            if (getData) {
+                this.commentIds.forEach((id) => {
+                    comments.push({
+                        id,
+                        top: (this.element.querySelector(`.vditor-comment[data-cmtids="${id}"]`) as HTMLElement).offsetTop,
+                    });
+                });
+                return comments;
+            }
         } else {
             return [];
         }
@@ -208,6 +218,7 @@ class WYSIWYG {
 
         this.element.addEventListener("scroll", () => {
             hidePanel(vditor, ["hint"]);
+            vditor.options.comment.scroll(vditor.wysiwyg.element.scrollTop);
             if (this.popover.style.display !== "block") {
                 return;
             }

+ 8 - 2
types/index.d.ts

@@ -511,8 +511,9 @@ interface IOptions {
     /** 评论 */
     comment?: {
         enable: boolean
-        add?(id: string, text: string): void
+        add?(id: string, text: string, commentsData: ICommentsData[]): void
         remove?(ids: string[]): void;
+        scroll?(top: number): void;
     };
     /** 主题。默认值: 'classic' */
     theme?: "classic" | "dark";
@@ -623,7 +624,7 @@ interface IVditor {
         preventInput: boolean,
         composingLock: boolean,
         commentIds: string[]
-        getComments(vditor: IVditor): string[],
+        getComments(vditor: IVditor, getData?: boolean): ICommentsData[],
         triggerRemoveComment(vditor: IVditor): void,
         showComment(): void,
         hideComment(): void,
@@ -643,3 +644,8 @@ interface IVditor {
         preventInput: boolean,
     };
 }
+
+interface ICommentsData {
+    id: string
+    top: number
+}

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor