Friendly Markdown

想象一下,你有一个允许用户输入评论的网站,并且你决定使用Markdown作为评论输入。你的目标群体大多知道如何使用Markdown,并且觉得它很方便。但是你可能也有一些非技术用户,对于他们来说,学习神秘的语法规则并不容易。

在不更改后端的情况下,您可以将ProseMirror作为替代输入编辑器。人们甚至可以在编辑时在两种视图之间切换!

prosemirror-markdown包定义了一个ProseMirrorschema,可以准确表达Markdown中可以表达的内容。它还带有一个解析器和序列化器,可以在此schema和Markdown文本之间转换文档。

为了抽象出实际的编辑器,我们首先在文本区域周围创建一个简单的接口:

class MarkdownView {
  constructor(target, content) {
    this.textarea = target.appendChild(document.createElement("textarea"))
    this.textarea.value = content
  }

  get content() { return this.textarea.value }
  focus() { this.textarea.focus() }
  destroy() { this.textarea.remove() }
}

然后为支持 Markdown 的 ProseMirror 实例实现相同的接口。此接口的输入和输出仍然是 Markdown 文本,它在内部将其转换为 ProseMirror 文档。

import {EditorView} from "prosemirror-view"
import {EditorState} from "prosemirror-state"
import {schema, defaultMarkdownParser,
        defaultMarkdownSerializer} from "prosemirror-markdown"
import {exampleSetup} from "prosemirror-example-setup"

class ProseMirrorView {
  constructor(target, content) {
    this.view = new EditorView(target, {
      state: EditorState.create({
        doc: defaultMarkdownParser.parse(content),
        plugins: exampleSetup({schema})
      })
    })
  }

  get content() {
    return defaultMarkdownSerializer.serialize(this.view.state.doc)
  }
  focus() { this.view.focus() }
  destroy() { this.view.destroy() }
}

最后,我们可以连接一些单选按钮,以允许用户在这两种表示之间切换。

let place = document.querySelector("#editor")
let view = new MarkdownView(place, document.querySelector("#content").value)

document.querySelectorAll("input[type=radio]").forEach(button => {
  button.addEventListener("change", () => {
    if (!button.checked) return
    let View = button.value == "markdown" ? MarkdownView : ProseMirrorView
    if (view instanceof View) return
    let content = view.content
    view.destroy()
    view = new View(place, content)
    view.focus()
  })
})