友好的 Markdown
Friendly Markdown
想象一下,你有一个允许用户输入评论的网站,并且你决定用 Markdown 作为评论的输入方式。你的目标用户大多熟悉 Markdown,并且觉得它很方便。但你可能也有一些非技术用户,对他们来说,学习晦涩的语法规则并不是一件自然而然的事。
Imagine you have a site that allows users to enter comments, and you've decided to use Markdown for the comment input. Your target group mostly knows how to use Markdown, and finds it convenient. But you may also have some non-technical users, for whom learning arcane syntactic rules does not come naturally.
无需修改任何后端代码,你就可以把 ProseMirror 作为一个替代的输入编辑器接入进去。用户甚至可以在编辑过程中随时在这两种视图之间切换!
Without changing anything in your backend, you can drop in ProseMirror as an alternative input editor. People can even switch between both views as they are editing!
prosemirror-markdown 包定义了一个 ProseMirror schema,它能够精确表达 Markdown 所能表达的内容。它还附带了一个解析器和序列化器,用于在该 schema 的文档与 Markdown 文本之间进行相互转换。
The
prosemirror-markdown
package defines a ProseMirror schema that can
express exactly the things that can be expressed in Markdown. It also
comes with a parser and serializer that convert documents in this
schema to and from Markdown text.
为了把真正的编辑器抽象出来,我们首先围绕一个 textarea 创建一个简单的接口:
To abstract the actual editor, we first create a simple interface around a textarea:
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 文档。
And then implement the same interface for a Markdown-enabled ProseMirror instance. The in- and output of this interface is still Markdown text, which it internally converts to a ProseMirror document.
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() }
}
最后,我们可以接上一些单选按钮,让用户在这两种表示之间切换。
Finally, we can wire up some radio buttons to allow users to switch between these two representations.
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()
})
})