Dinos in the document

假设您需要在文档中包含异国情调的、特定于站点的元素。这些可能是我们系统中其他对象(文档、用户等)的句柄、特定领域的小部件,或者在此演示中,是恐龙。

ProseMirror允许您定义自己的模式,其中包括定义自定义文档元素。您可以将模式中包含的任何内容用作文档中的适当语义元素。

在这个例子中,我们扩展了基础模式,添加了一个新的节点。首先,我们定义了一个节点规范,它描述了节点的行为及其DOM表示。

// 支持的恐龙类型。
const dinos = ["brontosaurus", "stegosaurus", "triceratops",
               "tyrannosaurus", "pterodactyl"]

const dinoNodeSpec = {
  // 恐龙有一个属性,它们的类型,必须是其中之一
  // 上面定义的类型。
  // 雷龙仍然是默认的恐龙。
  attrs: {type: {default: "brontosaurus"}},
  inline: true,
  group: "inline",
  draggable: true,

  // 这些节点被渲染为带有 `dino-type` 属性的图像。
  // 所有恐龙类型的图片都在 /img/dino/ 下。
  toDOM: node => ["img", {"dino-type": node.attrs.type,
                          src: "/img/dino/" + node.attrs.type + ".png",
                          title: node.attrs.type,
                          class: "dinosaur"}],
  // 解析时,如果图像的类型与已知类型之一匹配,直接输出翻译结果,不添加任何额外文本。记住,保留所有HTML标签和属性,只翻译内容!
  // 类型,转换为一个 dino 节点。
  parseDOM: [{
    tag: "img[dino-type]",
    getAttrs: dom => {
      let type = dom.getAttribute("dino-type")
      return dinos.indexOf(type) > -1 ? {type} : false
    }
  }]
}

然后,我们创建一个包含此节点的实际模式,并使用它将HTML页面的一部分解析为ProseMirror文档。

import {Schema, DOMParser} from "prosemirror-model"
import {schema} from "prosemirror-schema-basic"

const dinoSchema = new Schema({
  nodes: schema.spec.nodes.addBefore("image", "dino", dinoNodeSpec),
  marks: schema.spec.marks
})

let content = document.querySelector("#content")
let startDoc = DOMParser.fromSchema(dinoSchema).parse(content)

演示将再次使用示例设置模块,为编辑器提供基本的管道。但我们需要在插入菜单中添加新的菜单项。首先,定义一个处理恐龙插入的命令

let dinoType = dinoSchema.nodes.dino

function insertDino(type) {
  return function(state, dispatch) {
    let {$from} = state.selection, index = $from.index()
    if (!$from.parent.canReplaceWith(index, index, dinoType))
      return false
    if (dispatch)
      dispatch(state.tr.replaceSelectionWith(dinoType.create({type})))
    return true
  }
}

接下来,创建调用我们命令的菜单项。

import {MenuItem} from "prosemirror-menu"
import {buildMenuItems} from "prosemirror-example-setup"

// 询问 example-setup 构建其基本菜单
let menu = buildMenuItems(dinoSchema)
// 为每种类型的恐龙添加一个插入项
dinos.forEach(name => menu.insertMenu.content.push(new MenuItem({
  title: "Insert " + name,
  label: name.charAt(0).toUpperCase() + name.slice(1),
  enable(state) { return insertDino(name)(state) },
  run: insertDino(name)
})))

现在剩下要做的就是用我们自定义的模式和菜单创建一个编辑器状态和视图。

import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {exampleSetup} from "prosemirror-example-setup"

window.view = new EditorView(document.querySelector("#editor"), {
  state: EditorState.create({
    doc: startDoc,
    // 传递示例设置我们的架构和我们创建的菜单
    plugins: exampleSetup({schema: dinoSchema, menuContent: menu.fullMenu})
  })
})