Liyuan Li 5 anni fa
parent
commit
f1679b24ff
4 ha cambiato i file con 258 aggiunte e 1 eliminazioni
  1. 81 0
      demo/comment.html
  2. 170 0
      demo/comment.js
  3. 1 1
      src/ts/wysiwyg/input.ts
  4. 6 0
      webpack.start.js

+ 81 - 0
demo/comment.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="zh-cmn-Hans">
+<head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
+    <meta name="theme-color" content="#f1f7fe">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="mobile-web-app-capable" content="yes"/>
+    <meta name="apple-mobile-web-app-status-bar-style" content="black">
+    <title>Vditor: ♏ An In-browser Markdown editor, support WYSIWYG, Instant Rendering (Typora-like) and Split View
+        modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。</title>
+    <meta name="description"
+          content="Vditor 支持三种所见即所得(wysiwyg)、即时渲染(ir)、分屏预览(sv)模式,支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、多媒体、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染。"/>
+    <meta property="og:description"
+          content="Vditor 支持三种所见即所得(wysiwyg)、即时渲染(ir)、分屏预览(sv)模式,支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、多媒体、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染。"/>
+    <meta name="twitter:description" property="og:description" itemprop="description"
+          content="Vditor 支持三种所见即所得(wysiwyg)、即时渲染(ir)、分屏预览(sv)模式,支持大纲、数学公式、脑图、图表、流程图、甘特图、时序图、五线谱、多媒体、语音阅读、标题锚点、代码高亮及复制、graphviz 渲染。"/>
+    <link rel="dns-prefetch" href="//cdn.jsdelivr.net/"/>
+    <link rel="preconnect" href="https://cdn.jsdelivr.net">
+    <link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png"/>
+    <link rel="apple-touch-icon" href="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png">
+    <link rel="shortcut icon" type="image/x-icon" href="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png">
+    <meta name="copyright" content="B3log"/>
+    <meta http-equiv="Window-target" content="_top"/>
+    <meta property="og:locale" content="zh-cmn-Hans"/>
+    <meta property="og:title"
+          content="Vditor: ♏ An In-browser Markdown editor, support WYSIWYG,  Instant Rendering (Typora-like) and Split View modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。"/>
+    <meta property="og:site_name" content="Blog-vditor"/>
+    <meta property="og:url" content="https://b3log.org/vditor"/>
+    <meta name="twitter:card" content="summary"/>
+    <meta name="twitter:domain" content="b3log.org"/>
+    <meta name="twitter:title" property="og:title" itemprop="b3lig vditor"
+          content="Vditor: ♏ An In-browser Markdown editor, support WYSIWYG,  Instant Rendering (Typora-like) and Split View modes. 一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。"/>
+    <meta name="twitter:site" content="@B3logOS"/>
+    <meta name="twitter:url" content="https://b3log.org/vditor"/>
+    <meta property="og:image" content="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png"/>
+    <meta name="twitter:image" content="https://cdn.jsdelivr.net/npm/vditor/src/assets/images/logo.png"/>
+    <style>
+        body {
+            margin: 0;
+        }
+
+        .nav {
+            text-align: center;
+            margin: 20px 0
+        }
+
+        a {
+            color: #4285f4;
+        }
+
+        #vditor {
+            margin: 0 20px 20px;
+        }
+    </style>
+    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vconsole.min.js"></script>
+</head>
+<body>
+<div class="vditor-reset nav">
+    <a href="https://b3log.org/vditor" target="_blank">官网</a> &nbsp; &nbsp;
+    <a href="/render.html">Render</a> &nbsp; &nbsp;
+    <button onclick="window.vditor.setTheme('dark', 'dark',  'native');document.querySelector('body').style.backgroundColor='#2f363d'">
+        Dark Mode
+    </button> &nbsp; &nbsp;
+    <button onclick="window.vditor.setTheme('light', 'light', 'github');document.querySelector('body').style.backgroundColor=''">
+        Light Mode
+    </button>
+</div>
+<div style="display: flex">
+    <div id="vditor" style="flex: 1">
+        <h1>Vditor</h1>
+        <ul>
+            <li>foo</li>
+            <li>bar</li>
+        </ul>
+    </div>
+    <div id="comments" style="width: 300px"></div>
+</div>
+</body>
+</html>

+ 170 - 0
demo/comment.js

@@ -0,0 +1,170 @@
+import Vditor from '../src/index'
+import '../src/assets/scss/index.scss'
+
+// new VConsole()
+
+let toolbar
+if (window.innerWidth < 768) {
+  toolbar = [
+    'emoji',
+    'headings',
+    'bold',
+    'italic',
+    'strike',
+    'link',
+    '|',
+    'list',
+    'ordered-list',
+    'check',
+    'outdent',
+    'indent',
+    '|',
+    'quote',
+    'line',
+    'code',
+    'inline-code',
+    'insert-before',
+    'insert-after',
+    '|',
+    'upload',
+    'record',
+    'table',
+    '|',
+    'undo',
+    'redo',
+    '|',
+    'edit-mode',
+    'content-theme',
+    'code-theme',
+    'export',
+    {
+      name: 'more',
+      toolbar: [
+        'fullscreen',
+        'both',
+        'preview',
+        'info',
+        'help',
+      ],
+    }]
+}
+
+const bindCommentEvent = (cmtElement) => {
+  const inputElement = cmtElement.querySelector('input')
+  const id = cmtElement.getAttribute('data-id')
+  inputElement.addEventListener('blur', () => {
+    if (inputElement.value.trim() === '') {
+      window.vditor.removeCommentIds([id])
+      cmtElement.remove()
+    }
+  })
+  cmtElement.querySelector('button').addEventListener('click', () => {
+    window.vditor.removeCommentIds([id])
+    removeComment(cmtElement, id)
+  })
+
+  cmtElement.addEventListener('mouseover', () => {
+    window.vditor.hlCommentIds([id])
+  })
+
+  cmtElement.addEventListener('mouseout', () => {
+    window.vditor.unHlCommentIds([id])
+  })
+}
+
+const removeComment = (cmtElement, id) => {
+  cmtElement.remove()
+
+  let cmts = localStorage.getItem('cmts')
+  if (!cmts) {
+    return
+  } else {
+    cmts = JSON.parse(cmts)
+  }
+  cmts.find((item, index) => {
+    if (item.id === id) {
+      cmts.splice(index, 1)
+      return true
+    }
+  })
+  localStorage.setItem('cmts', JSON.stringify(cmts))
+}
+
+const renderComments = (ids) => {
+  let cmts = localStorage.getItem('cmts')
+  if (!cmts) {
+    localStorage.setItem('cmts', '[]')
+    cmts = []
+  } else {
+    cmts = JSON.parse(cmts)
+  }
+
+  ids.forEach(id => {
+    let text = ''
+    cmts.find((item) => {
+      if (item.id === id) {
+        text = item.text
+        return true
+      }
+    })
+
+    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)
+  })
+}
+
+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',
+  toolbar,
+  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>
+${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))
+    },
+    remove (ids) {
+      ids.forEach((id) => {
+        removeComment(document.querySelector(`#comments div[data-id="${id}"]`),
+          id)
+      })
+    },
+  },
+  after () {
+    renderComments(window.vditor.getCommentIds())
+  },
+})

+ 1 - 1
src/ts/wysiwyg/input.ts

@@ -188,7 +188,7 @@ export const input = (vditor: IVditor, range: Range, event?: InputEvent) => {
             && vditor.options.comment.enable && vditor.wysiwyg.commentIds.length > 0) {
             let commentIds: string[] = [];
             vditor.wysiwyg.element.querySelectorAll(".vditor-comment").forEach((item) => {
-                commentIds.concat(item.getAttribute("data-cmtids").split(" "));
+                commentIds = commentIds.concat(item.getAttribute("data-cmtids").split(" "));
             });
             commentIds = Array.from(new Set(commentIds));
 

+ 6 - 0
webpack.start.js

@@ -23,6 +23,7 @@ module.exports = {
     'index.js': './demo/index.js',
     'render.js': './demo/render.js',
     'jest-puppeteer.js': './demo/jest-puppeteer.js',
+    'comment.js': './demo/comment.js',
   },
   resolve: {
     extensions: ['.js', '.ts', '.png', '.scss'],
@@ -111,6 +112,11 @@ module.exports = {
       filename: './jest-puppeteer.html',
       template: './demo/jest-puppeteer.html',
     }),
+    new HtmlWebpackPlugin({
+      chunks: ['comment.js'],
+      filename: './comment.html',
+      template: './demo/comment.html',
+    }),
     new webpack.DefinePlugin({
       VDITOR_VERSION: JSON.stringify(pkg.version),
     }),