本手册/文档采用 GPT-4o + 人工方式翻译。每周检查一次原仓库或手动更新,欢迎 Star 和 PR
译者前言:
  1. 鼠标悬浮在中文上会出现英文原文,方便读者在觉得翻译质量不行的时候直接查看原文(欢迎 PR 更好的翻译)。
  2. 因为有些接口需要上下文,因此译者的增加了注释以对此进行额外的说明,以灰色背景块显示出来,代表了译者对某个接口的理解。
  3. 如果你觉得我的工作有帮助,可以 赏杯咖啡钱
  4. 欢迎关注我的技术/生活公众号「开二度魔法」,id:CoderXheldon

参考手册

本页面是富文本编辑器 ProseMirror 的 API 手册,它列出和描述了该库导出的全部接口。想了解更多关于它的介绍,请访问:指南

ProseMirror 由多个单独的模块构成。这个手册描述了每个模块导出的 API。例如,如果你想使用 prosemirror-state 模块,你可以像下面这样导入即可:

var EditorState = require("prosemirror-state").EditorState
var state = EditorState.create({schema: mySchema})

或者使用 ES6 的语法:

import {EditorState} from "prosemirror-state"
let state = EditorState.create({schema: mySchema})

prosemirror-state module

本模块实现了 ProseMirror 编辑器的 state 对象,以及关于选区 selection 和 插件 plugin 的抽象。

Editor State

ProseMirror 使用一个单独的大 对象 来保持对编辑器所有 state 的引用(基本上来说,需要创建一个与当前编辑器相同的编辑器)。 这个对象通过应用一个 transactions 来更新(即创建一个新的 state)。

transactions 按惯例在写代码的或者看源码的时候被缩写成 tr

class EditorState

ProseMirror 编辑器状态由此对象表示。一个 state 是一个持久化的数据结构--它本身并不更新,旧的 state 通过 apply 方法产生一个新的 state。

一个 state 有很多内建的字段,同时可以通过 plugins 来 定义 额外的字段。

doc: Node

当前文档

selection: Selection

当前选区。

storedMarks: readonly Mark[] | null

即将要应用到下一次输入的 marks。如果没有显式的设置 marks,此字段将会是 null。

schema: Schema

state 所表示的文档的 schema。

plugins: readonly Plugin[]

在当前 state 中激活的 plugins。

apply(trTransaction) → EditorState

对旧的 state 应用给定的 transaction 以产生一个新的 state。

applyTransaction(rootTrTransaction) → {stateEditorState, transactionsreadonly Transaction[]}

apply 的复杂版。该接口返回将应用到旧 state 以产生新 state 的每一个 transactions (其返回解构可能被插件的 transaction hooks 影响。)

tr: Transaction

从当前 state 生成一个新的 transaction 以对当前 state 进行修改。

该 transaction 是一个 getter 函数,每次调用都会 new 一个新的 transaction。
reconfigure(configObject) → EditorState

基于当前的 state 新建一个新的 state,只是新的 state 的中的字段会由传入的 plugins 重新配置。新旧两组 plugins 中的 state 字段中都存在的字段保持不变。 (相比于旧的 plugins 中)不再存在的字段将会被丢弃,新增的字段将会使用 plugin 的 state 对象的 init 方法进行初始化后作为新的 state 字段。

plugin 配置对象有一个 state 字段,其有两个方法,一个是 init 用来初始化 state;一个是 apply,用来决定如何更新 state。此 create 方法对于新增的 plugin 会调用其 state 的 init 方法进行初始化,以生成编辑器的 state。
config
plugins⁠?: readonly Plugin[]

新的激活的插件集合。

plugins 上的 state 构成新的编辑器的 state。
toJSON(pluginFields⁠?: Object<Plugin>) → any

将 state 对象序列化成 JSON 对象。如果你想序列化 plugin 的 state,则需要传递一个有着属性名-插件的映射关系的对象,该对象的属性名就会出现在返回值结果对象中。 参数也可以是字符串或者数字,但这种情况下参数会被忽略,以支持以 JSON.stringify 的方式调用 toString 方法。

如果想序列化 plugin 的 state,需要 plugin 的 state 对象有提供 toJSON 方法,该方法的参数是 plugin 的 key。doc 和 selection 是保留字段,不能作为参数对象的属性名。
static create(configEditorStateConfig) → EditorState

创建一个新的 state。

static fromJSON(
jsonany,
) → EditorState

反序列化一个 state 的 JSON 表示。config 至少应该有一个 schema 字段,并且应该包含用来初始化 state 的 plugin 数组。 pluginField 参数通过在 JSON 对象中的属性名与 plugin 实例对应的方式来反序列化 plugin 的 state。

pluginFields 中的属性名如果对应到了某个 plugin 的 key(string),则会调用对应 plugin 的 state 的 fromJSON 方法, 如果没有对应到任一个 plugin 的 key,则会直接调 plugin 的 state 的 init 方法,前者参数是 config、插件对应的 json 和根据 config 生成的编辑器 state;后者参数是 config 和根据 config 生成的编辑器的 state。
config
schema: Schema

反序列化用到的 schema。

plugins⁠?: readonly Plugin[]

激活插件的集合。

interface EditorStateConfig

传递给EditorState.create的对象类型。

schema⁠?: Schema

当前编辑器所使用的 schema。

doc⁠?: Node

起始文档。必须提供此文档或schema

selection⁠?: Selection

文档中可用的选区。

storedMarks⁠?: readonly Mark[]

stored marks 的初始集合。

plugins⁠?: readonly Plugin[]

state 中激活的 plugins。

class Transaction extends Transform

一个编辑器 state 的 transaction 可以被用来应用到 state 以创建一个更新的 state。 使用 EditorState.tr 来创建一个 transaction 实例。

EditorState.tr 是一个 getter 函数,每次调用都会 new 一个新的。

transaction 跟踪文档的更改(它们是Transform的子类),但也包括其他状态更改,如选择更新和storedMarks集合的调整。此外,您可以在 transaction 中存储 metadata属性,这些属性是客户端代码或插件可以使用的额外信息,用于描述 transaction 代表的内容,以便它们可以相应地更新其自己的状态

编辑器视图 使用了一些元数据属性:它会将一个属性 "pointer" 值为 true 附加到由鼠标或触摸输入直接引起的选择 transaction 上,一个 "composition" 属性持有一个 ID 标识符,标识由组合 DOM 输入引起的 transaction 的组合,以及一个 "uiEvent" 属性,可能是 "paste""cut""drop"

time: number

与当前 transaction 关联的时间戳,与 Date.now() 格式相同。

storedMarks: readonly Mark[] | null

当前 transaction 设置的 stored marks,如果有的话。

selection: Selection

该 transaction 的选区。默认是编辑器当前选区经过该 transaction mapped 后的选区,不过也会被 setSelection 方法给手动设置。

setSelection(selectionSelection) → Transaction

更新当前 transaction 的选区。其会决定 transaction 应用后编辑器的选区。

selectionSet: boolean

选区是否被该 transaction 显式更新过。

即在当前 transaction 中是否显式调用过 setSelection,一个 tr 在应用到 state 之前会 流过 所有的 plugin 的 apply 方法,因此这对于判断其他插件是否显式设置过选区很有用。
setStoredMarks(marksreadonly Mark[] | null) → Transaction

设置 stored marks。

ensureMarks(marksreadonly Mark[]) → Transaction

确保 transaction 设置的 stored marks 或者如果 transaction 没有设置 stored marks 的话,确保光标位置的 marks,与参数给定的 marks 一致。 如果一致的话什么也不做。

如果不一致的话,就让它们一致--这也是「确保」的含义。
addStoredMark(markMark) → Transaction

在已经设置的 stored marks 集合中增加一个 mark。

removeStoredMark(markMark | MarkType) → Transaction

在已经设置的 stored marks 集合中移除一个 mark 或者移除一种 mark。

storedMarksSet: boolean

当前 transaction 是否显式设置了 stored marks。

setTime(timenumber) → Transaction

更新该 transaction 的时间戳。

replaceSelection(sliceSlice) → Transaction

用给定的 slice 替换当前选区。

replaceSelectionWith(nodeNode, inheritMarks⁠?: boolean = true) → Transaction

用给定的 node 替换当前选区。如果 inheritMarks 是 true 并且 node 的内容是 inline 的话,插入的内容将会继承插入点位置的 marks。

deleteSelection() → Transaction

删除选区。

选区被删除了,其内容也一起被删除。
insertText(textstring, from⁠?: number, to⁠?: number) → Transaction

用包含给定文本的文本节点替换给定的 range,如果没有给定 range 的话则替换选区。

range 就是用 from 和 to 表示的一个范围。
setMeta(
valueany
) → Transaction

在该 transaction 上储存一个 metadata 信息,可以以 name 或者 plugin 来区分。

因为一个 transaction 可能会被不同的 plugin 设置不同的 metadata 信息,因此需要区分。key 可以传 PluginKey,或者简单一个字符串。
getMeta(keystring | Plugin | PluginKey) → any

用给定的 name 或者 plugin key 来获取设置的 metadata 信息。

给定的 name 或者 plugin key 就是上面 setMeta 设置的 key,获取的就是 setMeta 设置的 value。
isGeneric: boolean

如果该 transaction 没有包含任何 metadata 信息则返回 true,如此以来就可以被安全的扩展。

有些场景需要对 transaction 做一些额外处理,如合并多个 step,此时如果某个 step 有 metadata 信息,则说明该 step 对某个 plugin 可能有其他的用途,就不能简单的合并 step。
scrollIntoView() → Transaction

当该 transaction 更新完 state 后,让编辑器将选区滚动到视图窗口之内。

类似 chrome devtools 中,Elements 下对某个元素右键的 「Scroll into view」
scrolledIntoView: boolean

True when this transaction has had scrollIntoView called on it.

type Command = fn(
dispatch⁠?: fn(trTransaction),
) → boolean

命令是一些函数,它们接受一个状态和一个可选的 transaction 分发函数,并且...

  • 确定它们是否适用于该州
  • 如果没有,则返回 false
  • 如果传递了dispatch,则执行其效果,可能通过将 transaction 传递给dispatch
  • 返回真

在某些情况下,编辑器视图作为第三个参数传递。

Selection

一个 ProseMirror selection 可以是多种不同类型的选区。这个模块定义了一个基础文本选区 text selections(当然,光标是其中的一个特殊状态,即 selection 的内容为空) 和节点选区 node selections ,表示一个文档节点被选中。可以通过扩展 selection 父类来实现自定义的 selection 类型。

abstract class Selection

编辑器选区的超类。所有的选区类型都扩展自它。不应该直接实例化。

new Selection(
ranges⁠?: readonly SelectionRange[]
)

用给定的 head 和 anchor 和 ranges 初始化一个选区。如果没有 ranges 给定,则构造一个包含 $anchor$head 位置的 range。

$anchor: ResolvedPos

选区 resolved 过的 anchor 位置(即当选区变化的时候,其不动的一侧)。

$head: ResolvedPos

选区 resolved 过的 head 位置(即当选区变化时,移动的一侧)。

「选区变化时」可能是用户造成的,如用户用鼠标从左到右选择,则选区起始(左侧)是 anchor,即「锚点」;选区右侧(鼠标所在位置)是 head,即动点。
ranges: readonly SelectionRange[]

选区覆盖到的 ranges。

anchor: number

选区的 anchor 的位置。

head: number

选区的 head 的位置。

from: number

选区位置较小一侧的位置。

无论选区是如何选的,一般情况下 from 是选区的左侧起始位置。
to: number

选区位置较大的一侧。

无论选区是如何选的,一般情况下 to 是选区的右侧结束位置。
$from: ResolvedPos

resolve 过的选区的位置较小的一侧。

$to: ResolvedPos

resolve 过的选区的位置较大的一侧。

empty: boolean

表示选区是否包含任何内容。

abstract eq(selectionSelection) → boolean

测试当前选区与另一个选区是否相等。

abstract map(docNode, mappingMappable) → Selection

通过一个 mappable 对象来 map 当前选区。 doc 参数应该是我们正在 mapping 的新的 document。

一般通过 tr.doc 拿到将要 mapping 到的新的 document。
content() → Slice

获取选区内容的 slice 形式。

replace(
content⁠?: Slice = Slice.empty
)

用给定的 slice 替换当前选区,如果没有给 slice,则删除选区。该操作会附加到给定 transaction 最后。

替换后会将新的选区(光标)放到插入的内容的右侧。如果插入的内容是一个 inline 节点,则向右寻找该节点后面的位置。 如果不是 inline 节点,则向左寻找。
replaceWith(trTransaction, nodeNode)

用给定的 node 替换当前选区,该操作会附加到给定的 transaction 最后。

abstract toJSON() → any

将当前选区转换成 JSON 表示的格式。当在自己实现的 selection 类中实现此方法的时候,需要确保给这个返回的对象一个 type 属性, 属性值是你 注册 selection 时候的 ID。

getBookmark() → SelectionBookmark

获取一个选区的 bookmark,它是一个无需访问当前 document 即可被 mapped 然后再在 mapped 后通过给定一个 document 再解析成一个真实选区的值。(这个方法最可能被用在 history 中,以进行 选区追踪和恢复旧选区)该方法的默认实现仅仅是转换当前选区为一个文本选区,然后返回文本选区的 bookmark。

visible: boolean

控制该选区类型在浏览器中被激活的时候是否对用户可见。默认是 true

static findFrom(
textOnly⁠?: boolean = false
) → Selection | null

在给定的位置寻找一个可用的光标或叶节点选区,如果 dir 参数是负的则往左寻找,如果是正的则向右寻找。当 textOnly 是 true 的时候,则只考虑光标选区。 如果没有可用的选区位置,则返回 null。

此方法对在粘贴或者一番操作后,不知道应该将光标放到哪个合适的位置时的情况尤为有用,它会自动寻找一个合适的位置,而不用手动 setSelection,对此种情况还有用的一个方法是下面的 near 方法。
static near($posResolvedPos, bias⁠?: number = 1) → Selection

在给定的位置寻找一个可用的光标或者叶节点选区。默认向右搜索,如果 bias 是负,则会优先向左搜索。

static atStart(docNode) → Selection

寻找一个给定文档最开始的光标或叶节点选区。如果没有可用的位置存在,则返回 AllSelection

static atEnd(docNode) → Selection

寻找一个给定文档最末尾的光标或者叶节点选区。

static fromJSON(docNode, jsonany) → Selection

反序列化一个选区的 JSON 表示。必须在自定义的 selection 类中实现该方法(作为一个静态类方法)。

static jsonID(
selectionClass: {fromJSONfn(docNode, jsonany) → Selection}
) → {fromJSONfn(docNode, jsonany) → Selection}

为了能够从 JSON 中反序列化一个选区,自定义的 selection 类必须用一个字符串 ID 来注册自己,以消除歧义。 尽量要用一个不会与其他模块的类名冲突的字符串。

class TextSelection extends Selection

一个文本选区代表一个典型的编辑器选区,其有一个 head(移动的一侧)和一个 anchor(不动的一侧),二者都 指向一个文本块节点。它可以是空的(此时表示一个正常的光标位置)。

文本块节点,即文本节点的直接父节点。如定义了 doc > p > text,则文本块节点即 p 节点。
new TextSelection(
$head⁠?: ResolvedPos = $anchor
)

构造一个包含给定两点的文本选区。

$cursor: ResolvedPos | null

如果当前选区是一个光标选区(一个空的文本选区),则返回其 resolved 过的位置,否则返回 null。

static create(
docNode,
head⁠?: number = anchor
) → TextSelection

用一个非 resolved 过的位置作为参数来创建一个文本选区。

static between(
bias⁠?: number
) → Selection

返回一个跨越给定 anchor 和 head 位置的选区,如果它们不是一个文本位置,则调用 findFrom 就近寻找一个可用的文本选区。 bias 决定就近向哪个方向寻找,默认是向左,值为负时是向右。如果文档不包含一个可用的文本位置, 则调用 Selection.near 方法。

class NodeSelection extends Selection

一个 node (节点)选区是一个指向单独节点的选区。所有的配置为 selectable 的 node 节点都可以是一个 node 选区的目标。在这个类型的选区中,fromto 直接指向选择节点的前面和后面, anchor 等于 fromhead 等于 to

node 选区就是当选中一个节点的时候的选区类型。
new NodeSelection($posResolvedPos)

新建一个 node 选区。不会验证参数的可用性。

因为不会验证参数的可用性,所以需要保证参数 $pos 是一个 resolved 过的可用 pos。
node: Node

当前选择的 node。

static create(docNode, fromnumber) → NodeSelection

以一个未 resolved 过的位置来新建一个 node 选区。

static isSelectable(nodeNode) → boolean

判断给的节点是否可以被选中作为一个 node 选区。

class AllSelection extends Selection

代表了选中整个文档的选区类型(此时可能用文本选区类型来表示不是必要的,比如当一个文档开头或者结尾有一个叶节点的时候)。

new AllSelection(docNode)

创建一个覆盖给定文档的 AllSelection 选区类型。

class SelectionRange

表示文档中的一个选区范围。

new SelectionRange($fromResolvedPos, $toResolvedPos)

创建一个选取。

$from: ResolvedPos

选区范围位置较小的一侧。

$to: ResolvedPos

选区范围位置较大的一侧。

interface SelectionBookmark

一个轻量的,文档无关的选区形式。你可以对一个自定义选区类来自定义一个 bookmark 类型,使 history 正确处理它(自定义选区的 bookmark)。

map(mappingMappable) → SelectionBookmark

在一系列的文档修改后 map 该 bookmark 到一个新的 bookmark。

resolve(docNode) → Selection

将该 bookmark 再解析成一个真实选区。可能需要做一些错误检查,并且如果 mapping 后该 bookmark 变得不可用的话,则会回滚到 默认行为(通常是 TextSelection.between)。

Plugin System

为了让打包和扩展编辑器功能变得更容易,ProseMirror 提供了一个 Plugin 系统。

interface PluginSpec<PluginState>

这是一个传递给 Plugin 构造函数的类型。它提供了插件的配置。

props⁠?: EditorProps<Plugin<PluginState>>

该插件设置的 视图属性。属性如果是函数,则函数的 this 将绑定到当前实例。

对象属性是函数的话一般叫做对象的方法。
state⁠?: StateField<PluginState>

允许插件定义一个 state 字段,一个在编辑器整体 state 对象上的额外的插槽,其可以持有自己的插件 state。

key⁠?: PluginKey

可以被用来唯一确定一个 plugin。在一个给定的 state 你只能有一个给定 key 的 plugin。 你可以通过这个 key 而不用访问插件实例来访问该插件的配置和 state。

view⁠?: fn(viewEditorView) → PluginView

当插件需要与编辑器视图交互的时候,或者需要在 DOM 上设置一些东西的时候,使用这个字段。 当插件的 state 与编辑器 view 有关联的时候将会调用该函数。

filterTransaction⁠?: fn(trTransaction, stateEditorState) → boolean

如果有该函数,则该函数会在一个 transaction 被应用到 state 之前调用,以允许插件有机会取消该 transaction(通过返回 false)

appendTransaction⁠?: fn(
transactionsreadonly Transaction[],
) → Transaction | null | undefined

允许这个插件附加另一个 transaction 到将要被应用的 transactions 数组的末尾上去。 当另一个 plugin 又附加了一个 transaction 且其在当前 plugin 之后调用, 则当前 plugin 的该函数会再调用一次。但是仅含新的 transaction 和新的 state。也即是,它不会再将之前处理过的 transaction 再处理一次。

[string]: any

插件规格允许附加属性,可以通过Plugin.spec读取。

interface StateField<T>

插件可能会提供一个该配置类型的 state 字段(在它的 state 属性上)。 它描述了插件想要持有的 state。该字段下的方法调用的时候,其 this 指向插件实例。

init() → T

初始化插件的 state。config 是传递给 EditorState.create 的对象。 记住:instance 是一个半初始化的 state 实例,在当前插件之后初始化的插件在此时将不会有值。

因此在新建 state 的时候,插件的顺序至关重要。
apply(
valueT,
) → T

应用给定的 transaction 到插件的 state 字段,以产生一个新的 state。 记住,newState 参数再一次的,是一个部分构造的 state,它不会包含当前插件之后还未初始化的插件的 state。

toJSON⁠?: fn(valueT) → any

将当前字段值转换成 JSON。当然,你也可以留空以禁用当前插件 state 的序列化。

所谓转成 JSON,在该文档所有对象的 toJSON 方法都是转成一个 plain object,而不是 JSON.stringify 得到的对象。
fromJSON⁠?: fn(
valueany,
) → T

反序列化给定的该字段的 JSON 表示对象。记住:state 参数还是一个半序列化的 state 对象。

type PluginView

一个有状态的对象,可以通过插件安装在编辑器中。

update⁠?: fn(viewEditorView, prevStateEditorState)

编辑器 view 一更新就调用该函数。

编辑器 view 更新可能是用户的操作如输入内容,或者编辑器的操作,如由事件触发的 transaction 更新视图, 此处可以拿到编辑器的 view 和应用 transaction 之前的 state。
destroy⁠?: fn()

当 state 对象被重新配置而不再含该插件或者编辑器视图被销毁的时候调用该函数。

页面重载等情况会销毁编辑器的 view。

class Plugin<PluginState = any>

Plugins 可以被添加到 editor 中,它们是 编辑器 state 的一部分,并且能够影响包含它的 state 和 view。

new Plugin(specPluginSpec<PluginState>)

创建一个 plugin。

spec: PluginSpec<PluginState>

当前插件的 配置对象

props: EditorProps<Plugin<PluginState>>

当前插件导出的 属性

getState(stateEditorState) → PluginState | undefined

从编辑器的 state 上获取当前插件的 state。

class PluginKey<PluginState = any>

一个键用于标记插件,使得在给定的编辑器状态下可以找到它们。分配一个键意味着在一个状态中只能有一个该类型的插件处于活动状态。

new PluginKey(name⁠?: string = "key")

新建一个 plugin key

get(stateEditorState) → Plugin<PluginState> | undefined

用 key 从 state 获取到 key 对应的激活的插件。

getState(stateEditorState) → PluginState | undefined

从编辑器的 state 中获取插件的 state。

prosemirror-view module

ProseMirror 的视图模块在 DOM 中显示给定的 编辑器状态,并处理用户事件。

确保在使用此模块时将style/prosemirror.css作为样式表加载。

class EditorView

一个编辑器视图负责整个可编辑文档。它的 state 和行为由 props 决定。

新建编辑器的第一步就是 new 一个 EditorView。
new EditorView(
placeDOMNode |
{mountHTMLElement} |
,
)

新建一个 view 视图,place 参数可能是一个 DOM 节点,表示编辑器的挂载点,或者一个函数,则编辑器将会被挂载在文档根节点 或者一个对象,它的 mount 属性的值表示编辑器的挂载 DOM,而如果是 null,编辑器将不会被放到文档中。

place 是一个函数的时候,函数的参数是通过 document.createElement('div') 新建的一个 DOM 节点, 该节点将会作为函数的唯一参数传入,该节点还未被放入真实文档中,需要你手动放入。
state: EditorState

编辑器当前的 state

dom: HTMLElement

一个包含编辑器文档的可编辑 DOM 节点。(你不应该直接操作该节点的内容)

editable: boolean

指示当前编辑器是否 可编辑

dragging: {sliceSlice, moveboolean} | null

当编辑器的内容被拖拽的时候,这个对象包含有拖拽内容相关的信息及该内容是否被复制还是被移动。在其他时候,该对象是 null。

composing: boolean

composition 事件触发的时候,该值为 true。

composition 事件与 CJK 输入法有关,也与浏览器实现有关,Safari 和 Chrome 中的 composition 触发顺序就不一样,以及一些其他差异。 这导致了一些使用 ProseMirror 的编辑器在 Safari 上的表现比较诡异,论坛中也有很多针对 Safari 反馈的 bug,大多跟 composition 有关。
props: DirectEditorProps

编辑器 view 的 props(属性)

props 是一个 getter 属性,每次通过 view.props 访问到的 props 带的一定是最新的 state。
update(propsDirectEditorProps)

更新 view 的 props。将会立即引起 DOM 的更新。

setProps(propsPartial<DirectEditorProps>)

用给定的参数来更新已有的 props 对象,以达到更新 view 的目的。等同于 view.update(Object.assign({}, view.props, props))

updateState(stateEditorState)

单独更新编辑器 props 的 state 属性。

someProp<PropName extends keyof EditorProps, Result>() → Result | undefined

依次遍历一个 prop 的值,首先是直接提供的值,然后是视图中插件提供的值,再然后是状态中的插件提供的值(按顺序),每次找到非 undefined 的值时调用 f。当 f 返回一个真值时,立即返回该值。当未提供 f 时,将其视为恒等函数(直接返回 prop 值)。

若提供了 f 函数,则 f 函数执行的时候,参数即为遍历到的 prop 的值(一般是个函数), 若 f 函数返回了真值,则 someProp 函数的返回值即为 f 函数本身,并停止遍历;若 f 函数返回了非真值, 则继续遍历,直到遇到真值才返回。 若 f 函数未提供,则如果 prop 的值不为 undefined,则直接返回该值。
hasFocus() → boolean

查询当前 view 是否被 focus。

focus()

focus 编辑器。

这个过程会用到特性检测,即检查 dom.focus({preventScroll: true}) 是否支持。
root: Document | ShadowRoot

获取编辑器所在的根节点。通常情况下是顶级节点 document,但是也可能是一个 shadow DOM 根节点,如果编辑器在它内部的话。

updateRoot()

当现有编辑器视图移动到新文档或影子树时,调用此函数以重新计算其根。

posAtCoords(coords: {leftnumber, topnumber}) → {posnumber, insidenumber} | null

给定一对儿视口坐标信息,返回该坐标在文档中的位置。如果给定的坐标不在编辑器中,则会返回 null。 当返回一个对象时,pos 属性是离坐标最近的位置,inside 属性指示坐标落在的节点的内部节点的位置, 或者未为 -1,表示该坐标落在了顶级节点的位置,不在任何节点之内。

inside 属性举例:如果 table 结构是 table > td+ > p* > text*,则若 pos 落在了 text 内部,则 inside 就是 text 开头的位置;如果落在了 p 之前的位置(before),那就是 td 起始的位置(start)。
coordsAtPos(posnumber, side⁠?: number = 1) → {leftnumber, rightnumber, topnumber, bottomnumber}

返回给定文档位置的相对于视口的坐标及大小信息。leftright 总是相同,因为该函数返回的是一个光标的的位置和大小信息。 如果该位置在两个并不直接相邻的元素之间,则 side 参数决定了这两个元素哪个元素被使用,当 side 小于 0 的时候,使用位置前面(左边)的元素,否则是后面的元素。

光标只有高度没有宽度,因此只有 top 和 bottom 及 height 信息;left 和 right 总是一样的,width 总是 0.
domAtPos(posnumber, side⁠?: number = 0) → {nodeDOMNode, offsetnumber}

返回给定位置的 DOM 节点。如果 side 参数是负的,则寻找离 position 最近的前面(左边的元素) 否则就寻找离位置最近的右边的元素。如果是 0,则会更倾向于返回一个较浅(?)的位置。

记住:你 绝对不应该 直接修改编辑器内部的 DOM,而只能查看它(虽然即使是检查它也是不必要的)

查看它 的意思是只能获取 DOM 的信息,而不要设置。
nodeDOM(posnumber) → DOMNode | null

寻找给定位置的 DOM 节点。如果位置不指向一个 node 前面或者该 node 是一个不透明的 node view 的话,则返回 null。

该方法设计的目的是让你能够在 DOM 上调用类似 getBoundingClientRect 方法。绝对不要 直接修改编辑器的 DOM 元素,也不要通过这种方式添加样式之类的,因为你的修改可能随着节点的重绘被立即覆盖掉。

domAtPos 获取的是给定位置的 DOM 宽高和坐标信息,nodeDOM 获取的是给定位置的 DOM。你可以通过 nodeDOM 获取到 DOM 后再手动获取位置信息。
posAtDOM(
bias⁠?: number = -1
) → number

返回给定 DOM 的位置信息。(它会尽可能的优先选择直接检查文档结构来获取位置信息,而不是用四处寻找逐个探测的方式,但是有些情况下,比如给定的是一个事件 target,那你别无选择只能逐个 target 的进行测试)

这句话的意思是,如果你直接通过 ProseMirror 的接口,如 nodeDOM,通过 pos 获取到了 DOM,然后通过该方法相当于是一个逆过程,以获取到 pos。然而,如果你传给该函数的参数是来自于 event.target 那么 ProseMirror 只能通过挨个节点检查的方式,来确定它在 ProseMirror 的位置。

如果位置落在了一个叶子节点,那么 bias 参数可以用来决定使用叶子节点的哪一侧。

bias > 0 是右侧,否则是左侧,默认是左侧。
endOfTextblock(
dir"up" |
"down" |
"left" |
"right" |
"forward" |
"backward"
,
) → boolean

返回如果光标往给定方向移动的话,当前光标是否是一个文本 block 的末尾。例如,当给定方向为 「left」 的话,如果光标向左移动一个单位的距离将会离开文本 block,则会返回 true。 默认使用的是view 当前的 state,也可以传入一个不同的 state。

文本 block,一般情况下指的是 paragraph 这种的,以 text 为直接子元素的节点。该方法的移动给定方向后检测的位置是 state.selection.$head。
pasteHTML(htmlstring, event⁠?: ClipboardEvent) → boolean

运行编辑器的粘贴逻辑与给定的HTML字符串。 如果给定,event 将被传递到 handlePaste 钩子。

pasteText(textstring, event⁠?: ClipboardEvent) → boolean

运行编辑器的粘贴逻辑与给定的纯文本输入。

destroy()

从 DOM 中移除编辑器,并销毁所有的 node views

isDestroyed: boolean

这是真的,当视图已经 销毁(因此不应再使用)。

dispatchEvent(eventEvent)

用于测试。

dispatch(trTransaction)

派发一个 transaction。会调用 dispatchTransaction (如果设置了的话),否则默认应用该 transaction 到当前 state, 然后将其结果(新的 state)作为参数,传入 updateState 方法。该方法被绑定在 view 对象上,因此可以容易地被调用。

必须调用 view.dispatch(transaction) 才可以触发一个更改。该方法一般情况下用在事件响应函数里面,但是你也可以用在任何能访问到 view 的地方。 反过来说,比如在 plugin 的 state 的 apply 内,你访问不到 view,也就不能 dispatch 一个 tr。如果你强行在其内 dispatch 了一个 tr(如通过将 view 放到 window 作为全局访问的方法),那么会导致循环调用以致内存溢出。

Props

interface EditorProps<P = any>

Props 就是一些可以传递给编辑器的 view,或者用在插件中的值。这个接口列出了支持的 props。

不同的事件处理函数可能都返回 true 表示它们处理了相应的事件。view 将会在事件发生时帮你调用 preventDefault。但是 handleDOMEvents 中的事件需要你负责去手动调用。

不同的 prop 有不同的处理方式。prop 是函数的话则会在某个时刻调用:最开始的时候是寻找在 view 上的 prop,然后按照 plugin 书写的顺序查找其上的 prop,按顺序调用,直到它们中的某一个返回了 true 才终止。 而对于其他一些 porps,会使用遇到的第一个 prop 返回的值。

可选类型参数指的是 prop 函数中 this 的类型,并在定义插件时用于传入插件类型。

handleDOMEvents⁠?: {}

其是一个对象,键是 DOM 事件名,值是事件处理函数。事件处理函数将会先于 ProseMirror 处理任何发生在可编辑 DOM 元素上的事件之前调用。 与其他事件处理函数(此处指的是下面这些 ProseMirror 自己的事件处理函数)相反的是,当该函数返回 true 的时候,你需要手动调用 preventDefault(或者不调用,如果你想允许默认行为发生的话)

可以理解为,handleDOMEvents 中定义的事件比较原始,一切都需要你自己来掌控。之所以其内定义的事件处理函数会发生于 ProseMirror 事件处理之前,一个原因我猜是因为 如果 ProseMirror 事件处理完了之后再调用用户定义的事件处理函数,则需要再处理一遍 DOM 的更新。
handleKeyDown⁠?: fn(viewEditorView, eventKeyboardEvent) → boolean | undefined

当编辑器接收到一个 keydown 事件的时候调用。

handleKeyPress⁠?: fn(viewEditorView, eventKeyboardEvent) → boolean | undefined

当编辑器接收到一个 keypress 事件的时候调用。

handleTextInput⁠?: fn() → boolean | undefined

无论何时用户直接输入了文字的时候,该处理函数将会在输入内容应用到 DOM 之前调用。如果该函数返回 true,则用户输入文本到编辑器的默认行为将会被阻止。

该方法通常用来拦截输入,然后生成新的输入,如自动转换 markdown 语法,或者按下某个键执行特殊操作的时候比较有用。
handleClickOn⁠?: fn() → boolean | undefined

为每一个点击事件冒泡路径上的节点从内到外都调用一遍该函数。如果是内部节点,则 direct 将会是 true。

handleClick⁠?: fn() → boolean | undefined

当编辑器被点击的时候调用,函数执行顺序位于 handleClickOn 函数之后。

handleDoubleClickOn⁠?: fn() → boolean | undefined

handleClickOn,只是针对双击事件。

handleDoubleClick⁠?: fn() → boolean | undefined

handleClick 只是针对双击事件。

handleTripleClickOn⁠?: fn() → boolean | undefined

handleClickOn,只是针对三击事件。

handleTripleClick⁠?: fn() → boolean | undefined

handleClick 只是针对三击事件

handlePaste⁠?: fn() → boolean | undefined

可以用来覆盖默认的粘贴行为。slice 是被编辑器格式化后的粘贴内容,不过你也可以通过直接访问事件对象来获取原始的粘贴内容。

粘贴事件中的数据位于 event.dataTransfer 对象上。
handleDrop⁠?: fn() → boolean | undefined

当有东西被放入编辑器的时候调用。如果是从当前编辑器选区放入的,则 moved 参数会是 true(因此选区的内容应该被删除)。

handleScrollToSelection⁠?: fn(viewEditorView) → boolean

当 view 更新了 state 之后,尝试将选区滚动到视图中的时候调用该函数。该函数可能返回 false,表示它不处理滚动;或者返回 true,表示让默认行为发生。

createSelectionBetween⁠?: fn() → Selection | null

在给定的起点和终点新建一个选区。

domParser⁠?: DOMParser

parser 用来从 DOM 中读取编辑器的变化。默认情况下(如果不设置的话)调用 DOMParser.fromSchema 方法,参数是编辑器的 schema。

transformPastedHTML⁠?: fn(htmlstring, viewEditorView) → string

可以被用来在 HTML 文本被 parser 之前 转换一下。

clipboardParser⁠?: DOMParser

用来从粘贴板中读取内容后 parser 。如果没有给,则使用 domParser 属性。

transformPastedText⁠?: fn() → string

转换粘贴的纯文本。如果粘贴的文本是纯文本的话,plain 将会是 true。

clipboardTextParser⁠?: fn() → Slice

将粘贴板中的文本 parse 成文档 slice。将会在 transformPastedText 之后调用。 默认行为是将文本分割成多行,然后使用 <p> 标签包裹之,然后再对其调用 [clipboardParser](#view.EditorProps.clipboardParser)。如果粘贴的内容是纯文本,则plain` 将会是 true。

transformPasted⁠?: fn(sliceSlice, viewEditorView) → Slice

可用于在粘贴或拖放内容之前对其进行转换,然后再应用到文档中。

transformCopied⁠?: fn(sliceSlice, viewEditorView) → Slice

可用于在内容被序列化到剪贴板之前转换复制或剪切的内容。

nodeViews⁠?: Object<NodeViewConstructor>

允许您为节点传递自定义渲染和行为逻辑。应将节点名称映射到生成实现节点显示行为的NodeView对象的构造函数。第三个参数getPos是一个可以调用以获取节点当前位置的函数,这在创建transaction以更新节点时非常有用。请注意,如果节点不在文档中,此函数返回的位置将是undefined

decoration 是一个在当前 node 周围激活的 node decoration 或者 inline decoration 数组。 他们会自动绘制,通常情况下你可以忽略它们,不过它们也可以用来为 node view 提供上下文信息,而不是将它们添加到文档中。

最后一句话的意思是,在 plugin.props 的 decoration 属性上,你可以通过构造 decoration 的时候添加一些额外的信息,然后在 node view 中拿到这些信息来搞事情。

innerDecorations 指向节点内容的装饰器。如果你的 view 没有内容或者没有 contentDOM 属性的话,你可以安全的忽略它,因为 编辑器将会把该 decorations 绘制到其内容上。不过如果你的 view 有内容,比如,用该内容创建一个嵌套的 editor,那么提供给其内部元素的内部 decorations 就是有用的。

这个 innerDecorations 是新增的参数,之前是没有这个参数的,可以看下原仓库的修改记录。

(出于向后兼容性的原因,标记视图也可以包含在此对象中。)

markViews⁠?: Object<MarkViewConstructor>

传递自定义标记渲染函数。请注意,这些不能提供节点视图可以提供的那种动态行为——它们只是提供自定义渲染逻辑。第三个参数表示标记的内容是否为内联。

clipboardSerializer⁠?: DOMSerializer

用于将内容放入剪贴板的DOM序列化器。如果未提供,将使用 DOMSerializer.fromSchema 的结果。此对象将仅调用其 serializeFragment 方法,您可以提供实现兼容方法的替代对象类型。

clipboardTextSerializer⁠?: fn(contentSlice, viewEditorView) → string

当复制内容到粘贴板的时候,该方法将会被调用以用来获取选区内的文本。默认情况下,编辑器会在选区范围使用 textBetween 方法。

decorations⁠?: fn(stateEditorState) → DecorationSource | null | undefined

一个展示在 view 上的 document decorations(文档装饰器) 集合。

之前该方法返回 DecorationSet,现在返回 DecorationSource,有兴趣的可以研究下区别。
editable⁠?: fn(stateEditorState) → boolean

当它返回 false,那么 view 的内容不能直接编辑。

不能直接编辑的意思就是用户不能将光标放入进去,然后编辑。但是仍然可以通过 dispatch transaction 进行编辑。
attributes⁠?: Object<string> |

控制可编辑元素上的 DOM attributes。可以是一个对象,或者是一个函数接收编辑器的 state,然后返回一个对象。 默认情况下,元素将会被设置一个 「ProseMirror」 类名,以及一个由 editable prop 决定的 contentEditable attributes。 在此处提供的其他类名将会被附加上去。对于其他 attributes,最先提供的将会被使用(就像 someProp 一样)。

原文中的 prop 和 attribute 我个人觉得应该分开翻译而不能都翻译成 属性,但是找不到合适的中文进行区分, 部分翻译资料(如上古时期的 jQuery),将 attr 和 prop 分别翻译成「属性」和「特性」,在此处感觉也不是很妥当,因此索性不翻译了。
scrollThreshold⁠?: number |
{topnumber, rightnumber, bottomnumber, leftnumber}

当滚动光标位置到视口的时候,决定光标与视口尾部的距离(单位是像素)多大才开始滚动。默认是 0。

scrollMargin⁠?: number |
{topnumber, rightnumber, bottomnumber, leftnumber}

当光标滚动到视口中的时候,决定光标离视口上下方的距离,默认是 5(像素为单位)。

type NodeViewConstructor = fn(
getPosfn() → number | undefined,
decorationsreadonly Decoration[],
) → NodeView

提供用于创建节点视图函数类型

type MarkViewConstructor = fn() → MarkView

函数类型用于创建标记视图。

interface DirectEditorProps extends EditorProps

直接提供给编辑器视图的props对象支持一些不能在插件中使用的字段:

state: EditorState

编辑器当前的 state。

plugin 有自己的 state 字段,其与 props 平级,因此不作为 props 的属性。
plugins⁠?: readonly Plugin[]

一组在视图中使用的插件,应用它们的插件视图属性。传递带有状态组件(状态字段transaction过滤器或附加器)的插件将导致错误,因为此类插件必须存在于状态中才能工作。

dispatchTransaction⁠?: fn(trTransaction)

view dispatch 一个 transaction 后(更新 state 前),transaction 会先经过此回调函数。 如果你设置了该函数,你应该需要保证该函数以调用 view 的 updateState 方法结束。 updateState 方法接受一个 applied 过该 transaction 的 state 作为参数。回调的 this 绑定到 view 实例上。

interface NodeView

默认情况下,文档节点使用它们 schema 配置对象中的 toDOM 方法来渲染,然后完全由编辑器管理状态。而对于一些使用场景,比如为特定节点嵌入编辑界面等情况, 你可能想要更加细粒度的控制一个节点在编辑器中的表现形式,因此,你需要 define 一个自定义的 node view。

node views 们返回的对象必须保证有以下接口:

dom: DOMNode

表示文档节点的外部DOM节点。

contentDOM⁠?: HTMLElement

应该持有节点的内容的 DOM 节点。只有当定义了 dom 属性且节点类型不是叶子节点的时候才有意义。 当它设置的时候,ProseMirror 将会将节点的子节点渲染到该 DOM 中作为它的子节点; 如果没有设置,node view 本身有责任渲染(或者决定不渲染)它的子节点。

update⁠?: fn(
decorationsreadonly Decoration[],
) → boolean

当提供时,这将在视图更新自身时被调用。它将被提供一个节点、一个围绕该节点的活动装饰数组(这些装饰会自动绘制,节点视图如果对其不感兴趣可以忽略),以及一个表示适用于节点内容的任何装饰的装饰源(同样可以被忽略)。如果它能够更新到该节点,应返回 true,否则返回 false。如果节点视图具有contentDOM属性(或没有dom属性),其子节点的更新将由 ProseMirror 处理。

multiType⁠?: boolean

默认情况下,update 仅在与此视图位置相同的节点类型出现时被调用。当你将此设置为 true 时,它将对任何节点进行调用,从而可以拥有表示多种节点类型的节点视图。你需要检查在 update 中接收到的节点类型,并对无法处理的类型返回 false

selectNode⁠?: fn()

可以用来覆盖节点选中的展示状态(作为一个节点选区)。

deselectNode⁠?: fn()

当定义一个 selectNode 方法,你应该同时提供一个 deselectNode 方法去移除前者所做的效果。

setSelection⁠?: fn()

该方法将会被调用以用来处理节点内部选区的设置。anchorhead 位置相对于节点的起始位置。 默认情况下,将会在与这些位置相对应的 DOM 位置之间创建 DOM 选区,不过你可以通过覆盖该行为来做一些其他的事情。

stopEvent⁠?: fn(eventEvent) → boolean

可以用来阻止编辑器处理一些或者所有的源自 node view 的 DOM 事件。返回 true 表示编辑器不处理该事件。

ignoreMutation⁠?: fn(mutationViewMutationRecord) → boolean

当视图内发生变异时调用。如果编辑器应该重新读取选择或重新解析变异周围的范围,请返回false;如果可以安全地忽略,请返回true。

destroy⁠?: fn()

当节点视图从编辑器中移除或整个编辑器被销毁时调用。

interface MarkView

默认情况下,文档标记使用其规范的 toDOM 方法的结果进行渲染,并完全由编辑器管理。对于某些用例,您需要对标记的编辑器内表示的行为进行更多控制,并需要 定义 自定义标记视图。

对象作为标记视图返回时必须符合此接口。

dom: DOMNode

表示文档节点的外部DOM节点。

contentDOM⁠?: HTMLElement

应包含标记内容的 DOM 节点。当此属性不存在时,dom 属性用作内容 DOM。

ignoreMutation⁠?: fn(mutationViewMutationRecord) → boolean

变异在视图内发生时调用。如果编辑器应该重新读取选择或重新解析变异周围的范围,则返回false;如果可以安全地忽略,则返回true。

destroy⁠?: fn()

当标记视图从编辑器中移除或整个编辑器被销毁时调用。

type ViewMutationRecord = MutationRecord |
{type"selection", targetDOMNode}

一个 ViewMutationRecord 表示视图内发生的 DOM 变异 或选择变化。当变化是选择变化时,记录将具有一个 type 属性为 "selection"(对于原生变异记录不会出现这种情况)。

interface DOMEventMap extends HTMLElementEventMap

帮助器类型将事件名称映射到事件对象类型,但包括 TypeScript 的 HTMLElementEventMap 不知道的事件。

[string]: any

Decorations

装饰使得可以在不实际更改文档的情况下影响文档的绘制方式。

class Decoration

Decoration(装饰器)对象可以通过 decoration 属性提供给 view。 它们有多个不同的变体,有关的详细信息,参见此类的静态成员。

Decoration 有三种,widget 挂件装饰器;inline 行内装饰器;node 节点装饰器;
from: number

decoration 开始的位置

to: number

decoration 结束的位置。如果是 widget decorations 的话,该值将会和 from 一致。

spec: any

当创建 decoration 的时候提供的配置。用来存储一些额外的信息非常有用。

static widget(
toDOMfn(
getPosfn() → number | undefined
) → DOMNode | ,
spec⁠?: Object
) → Decoration

创建一个 widget decorations,它是一个显示在给定位置的 DOM 节点。推荐的方式是通过传递一个函数来返回 decoration,以实现当该 decoration 绘制在 view 的时候延迟渲染的目的,不过你也可以直接传递一个 DOM 节点。getPos 方法用来获取 widget 在当前文档的位置。

spec
side⁠?: number

控制该 widget 与文档位置的哪一侧相关。当是负数的时候,它绘制在给定位置光标的之前, 并且在该位置插入的内容在 widget 之后。当非负(默认)的时候,widget 绘制在给定位置光标之后,用户输入的内容会插入到该位置之前。

当在同一个位置有多个 widget 的时候,他们的 side 决定了它们出现的顺序。较小的值出现在前面。 相同 side 值的话先后位置不确定。

相同 side 值 widget 出现的先后位置不确定,原因跟某些算法排序的 稳定 概念类似。

marks 是 null 的时候,side 同样决定 widget 包裹的 marks。节点之前的是负的,节点之后的是正的。

marks⁠?: readonly Mark[]

绘制在 widget 周围的 marks。

stopEvent⁠?: fn(eventEvent) → boolean

可以用来控制编辑器应该忽略从 widget 冒泡出来的哪些 DOM 事件。

ignoreSelection⁠?: boolean

当设置的时候(默认是 false),在 widget 内的选区变化将被忽略, 这样的话该变化就不会让 ProseMirror 尝试重新同步该选区和 state 的选区。

key⁠?: string

当比较此种类型的 decorations 的时候(以决定它是否应该被重绘),ProseMirror 将会默认通过 widget DOM 节点来识别。如果你传递了一个 key,那它就会用 key 来对比。 这对于你仅仅想在内存中创建 decorations 而不真正绘制 DOM 结构很有用。确保任何具有相同 key 的 widget 是可互换的--比如,如果 widget 的一些事件处理函数不一样,即使 DOM 结构相同,也应该有不同的 key。

destroy⁠?: fn(nodeDOMNode)

在小部件装饰被移除或编辑器被销毁时调用。

[string]: any

规格允许任意附加属性。

static inline(
spec⁠?: Object
) → Decoration

创建一个内联的 decoration,它会在 fromto 之间的每一个内联节点上添加给定的 attributes。

spec
inclusiveStart⁠?: boolean

决定如果内容直接插入在这个位置的时候,decoration 的左侧如何 mapped。 默认情况下,decoration 不会包括新的内容,不过你可以设置为 true 来让它影响新内容。

inclusiveEnd⁠?: boolean

决定 decoration 的右侧如何被 mapped。具体看 inclusiveStart

[string]: any

规格可能具有任意的附加属性。

static node(
spec⁠?: any
) → Decoration

创建一个 node decoration。fromto 应该精确的指向在文档中的某个节点的前面和后面。该节点,也只有该节点,会受到给定的 attributes。

type DecorationAttrs

一个被用来添加到被装饰的节点附近的 attributes 集合。大多数 properties 的名字与同名的 DOM attributes 一样,以用来被设置为属性值。下面几个是特例:

nodeName⁠?: string

如果该值为非 null,则目标节点将会用该类型的节点包裹住(同时其他的属性会被应用到该元素上)。

class⁠?: string

会被 添加 到节点已有的类名上的 CSS 的类名,或者用空格分隔的 css 类名集合。

style⁠?: string

会被 添加 到节点已有的 style 属性上的 CSS 字符串。

[string]: string | undefined

任何其他属性都被视为常规DOM属性。

class DecorationSet implements DecorationSource

一个 decorations 集合,用这种数据结构组织它们可以让绘制算法高效的对比和渲染它们。 它是一个不可突变的数据结构,它不改变,更新会产生新的值。

find(
start⁠?: number,
end⁠?: number,
predicate⁠?: fn(specany) → boolean
) → Decoration[]

找到给定范围涉及到的所有的 decoration 集合(包括开始或结束位置在边界的 decorations), 然后用给定的 predicate 函数来检测是否匹配,该函数参数是 decoration 的配置对象。 若 startend 省略,则集合中所有的 decoration 将会被检测。如果 predicate 没有给出,则所有的 decorations 将会 match。

map(mappingMapping, docNode, options⁠?: Object) → DecorationSet

Map decorations 的集合以响应文档修改。

options
onRemove⁠?: fn(decorationSpecany)

当设置该函数的时候,该函数会对在 mapping 过程中被移除的 decoration 调用该函数,传递 decoration 的配置对象。

add(docNode, decorationsDecoration[]) → DecorationSet

将给定的装饰数组添加到集合中的装饰中,生成一个新的集合。消耗decorations数组。需要访问当前文档以创建适当的树结构。

remove(decorationsDecoration[]) → DecorationSet

用当前的 decorations 集合减去给定数组中的 decorations,得到一个新的 decorations 集合。

static create(docNode, decorationsDecoration[]) → DecorationSet

创建一组装饰,使用给定文档的结构。这将消耗(修改)decorations数组,因此如果需要保留它,必须制作一个副本。

static empty: DecorationSet

decorations 的空集合。

interface DecorationSource

一个可以提供 decorations 的对象。被 DecorationSet 实现,会被传给 node views 方法。

map(mappingMapping, nodeNode) → DecorationSource

Map decorations 的集合以响应文档修改。

forChild(offsetnumber, childNode) → DecorationSource

提取一个DecorationSource,其中包含给定偏移量处给定子节点的装饰。

forEachSet(ffn(setDecorationSet))

为组中的每个装饰集调用给定函数。

prosemirror-model module

这个模块定义了 ProseMirror 的内容模型,它的数据结构被用来表示文档和其内的节点并让它们按预期工作。

Document Structure

一个 ProseMirror 的文档是一个树状结构。在每个层级中,一个 node 描述了内容的类型,同时通过 fragment 来保持对其子节点的引用。

class Node

这个类表示 ProseMirror 中组成文档树的节点,因此一个文档就是一个 Node 的实例,以及它的子节点同样是 Node 的实例。

节点都是一些持久化的数据结构。每次更新会创建一个新的节点与一些你想要的内容,而不是改变旧的节点。旧的节点始终保持旧文档的引用。 这使得在旧的和新的数据之间共享结构变得容易,因为像这样的树状结构没有「向后的指针」(?)

不要 直接修改 Node 对象的属性。查看 中文指南获取更多信息。

type: NodeType

当前节点的类型。

attrs: Attrs

一个键值对。允许的和需要的 attributes 取决于 节点类型。

marks: readonly Mark[]

应用到当前节点的 marks(marks 是一些类似于加粗或者链接一样的节点)

content: Fragment

一个持有节点子元素的容器。

该容器是 Fragment 类型
children: readonly Node[]

此节点的子节点数组。

text: string | undefined

对于文本节点,它包含了节点的文本内容。

nodeSize: number

表示该节点的大小,由基于整数的 indexing scheme 决定。 对于文本节点,它是字符数,对于其他叶子节点,是 1。对于非叶子节点,它是其内容的大小加上 2(开始和结束标签)。

indexing scheme 链接指向中文翻译指南,请搜索 Document 一节 下的 Indexing 一节。
childCount: number

该节点拥有的子节点个数。

child(indexnumber) → Node

获取给定 index 处的子节点。如果 index 超出范围,则返回错误。

maybeChild(indexnumber) → Node | null

获取给定 index 的子节点,如果存在的话。

不存在返回 undefined。
forEach()

为每一个子节点调用 f 函数,参数是子节点、子节点相对于当前节点的偏移、以及子节点的 index。

nodesBetween(
startPos⁠?: number = 0
)

在给定的两个相对于此节点内容起始位置之间递归地为所有后代节点调用回调函数。回调函数以节点、其相对于原始节点(方法接收者)的位置、其父节点及其子索引为参数。当回调函数对某个节点返回false时,该节点的子节点将不会被递归。最后一个参数可用于指定一个起始位置进行计数。

descendants()

对每一个后代节点调用给定的回调函数 f。当回调处理一个节点的时候返回 false ,则后续不会继续对该节点的子节点再调用该回调了。

上述递归都是深度优先。
textContent: string

该节点的所有文本内容连接为一个字符串返回。

textBetween(
leafText⁠?: string | fn(leafNodeNode) → string | null
) → string

获取位置fromto之间的所有文本。当提供blockSeparator时,它将被插入以分隔来自不同块节点的文本。如果提供了leafText,它将被插入到遇到的每个非文本叶节点中,否则将使用leafText

firstChild: Node | null

返回节点的第一个子节点,如果没有则是 null

lastChild: Node | null

返回节点的最后一个子节点,如果没有则是 null

eq(otherNode) → boolean

测试两个节点是否表示的是文档中相同的部分。

比较的手段是先比较节点的引用,如果相等直接为 true;否则比较 markup 是否相等,如果不是则返回 false,如果是再递归比较二者的子节点。
sameMarkup(otherNode) → boolean

比较当前与给定节点的 markup(包含类型、attributes 和 marks)是否相等。如果相同返回 true

hasMarkup(
attrs⁠?: Attrs,
marks⁠?: readonly Mark[]
) → boolean

检查节点是否有给定的类型、attributes 和 marks。

copy(content⁠?: Fragment | null = null) → Node

新建一个与当前节点有相同 markup 的节点,包含给定的内容(如果没有给定内容则为空)。

mark(marksreadonly Mark[]) → Node

新建一个当前节点的副本,包含给定的 marks,而不是当前节点原始的 marks。

cut(fromnumber, to⁠?: number = this.content.size) → Node

创建一个当前节点的副本,该节点仅包含给定位置范围的内容。如果 to 没有给定,则默认是当前节点的结束位置。

slice(
to⁠?: number = this.content.size,
includeParents⁠?: boolean = false
) → Slice

剪切文档给定位置范围的部分,然后作为 Slice 对象返回。

replace(fromnumber, tonumber, sliceSlice) → Node

用给定的 slice 替换给定位置范围的文档内容。slice 必须「适合」该位置范围,也就是说,slice 打开的两侧必须能够正确的连接它两侧被切开的周围的内容, 同时它的子节点也必须符合放入位置的祖先节点的 scheme 约束。如果有任何违背,那么将会抛出一个 ReplaceError

nodeAt(posnumber) → Node | null

返回给定位置右侧的节点。

childAfter(posnumber) → {nodeNode | null, indexnumber, offsetnumber}

如果有的话,返回给定偏移量后面的直接子节点,同时返回它的 index 以及相对于当前节点的偏移。

childBefore(posnumber) → {nodeNode | null, indexnumber, offsetnumber}

如果有的话,返回给定偏移量前面的直接子节点,同时返回它的 index 以及相对于当前节点的偏移。

resolve(posnumber) → ResolvedPos

resolve 文档中给定的位置,返回一个关于此位置上下文信息的 object

rangeHasMark() → boolean

测试文档中给定的位置范围内是否有给定的 mark 或者 mark 类型。

isBlock: boolean

是否是一个块级节点(非内联节点的都是块级节点)。

isTextblock: boolean

是否是一个文本块节点(textblock),即有内联内容的块级节点。

inlineContent: boolean

节点是否允许内联内容。

isInline: boolean

节点是否是内联节点(文本节点或者能够出现在文本之间的节点都是内联节点)。

isText: boolean

是否是文本节点。

isLeaf: boolean

是否是一个叶子节点。

isAtom: boolean

是否是一个原子节点,例如,它没有一个直接可编辑的内容。它的值通常与 isLeaf 一样,不过可以通过节点配置对象上的 atom 属性 进行设置。 (典型的使用场景是节点展示成一个不可编辑的 node view)。

toString() → string

为了 debug 目的获取当前节点的字符串表示。

contentMatchAt(indexnumber) → ContentMatch

获取当前节点给定 index 的 content match。

content match 在 ProseMirror 中也是一个专有名词。
canReplace(
replacement⁠?: Fragment = Fragment.empty,
start⁠?: number = 0,
end⁠?: number = replacement.childCount
) → boolean

测试用给定的 fragment(默认是空的 fragment) 替换 fromto(from 和 to 是子节点位置 index) 之间的内容是否合法(即符合 schema 约束)。 你可以可选的传入 startend(start 和 end 都是子节点的位置 index)以只用 fragment 的一部分替换。

canReplaceWith(
marks⁠?: readonly Mark[]
) → boolean

测试用给定的节点类型替换当前节点 fromto index 之间的子元素是否合法。

canAppend(otherNode) → boolean

测试给定节点的内容可以被附加到当前节点最后。如果给定节点是空的,那么只有当至少一个节点类型能够出现在这两个节点之内的时候才会返回 true(以避免合并完全不兼容的节点)。

check()

检查此节点及其后代是否符合 scheme 约束,如果不符合则引发异常。

toJSON() → any

返回一个当前节点 JSON 序列化的表示。

不像我们认为的 JSON 序列化后与 stringify 过一样是个字符串,这里的序列化后是个对象。
static fromJSON(schemaSchema, jsonany) → Node

从一个节点 JSON 序列化的对象中反序列化出 Node 节点。

class Fragment

一个 fragment 表示了节点的子节点集合。

像 nodes 一样,fragment 也是一个持久化数据结构,你不应该直接修改他们或者他们的内容,而应该创建一个新的实例。下面的 API 就是用来试图将这件事变得容易。

size: number

fragment 的大小,也即它的内容节点大小的总和。

content: readonly Node[]

此片段中的子节点。

nodesBetween(
nodeStart⁠?: number = 0,
parent⁠?: Node
)

对相对于 fragment 开始位置的两个位置范围内的节点调用 f 回调。如果某个节点的回调返回 false,则不会对该节点的内部节点再调用该回调了。

descendants()

为每个子节点调用给定的回调函数。pos将相对于片段的起始位置。回调函数可以返回false以阻止遍历给定节点的子节点。

textBetween(
leafText⁠?: string | fn(leafNodeNode) → string | null
) → string

提取fromto之间的文本。请参阅Node上的相同方法。

append(otherFragment) → Fragment

创建一个包含当前 fragment 内容和给定 fragment 内容的新的 fragment。

cut(fromnumber, to⁠?: number = this.size) → Fragment

从 fragment 剪切出给定范围的一个子 fragment。

replaceChild(indexnumber, nodeNode) → Fragment

将 fragment 中的给定 index 位置的节点用给定节点替换掉后,创建一个新的 fragment。

addToStart(nodeNode) → Fragment

将给定节点添加到此片段的前面以创建一个新的片段。

addToEnd(nodeNode) → Fragment

通过将给定节点附加到此片段来创建一个新片段。

eq(otherFragment) → boolean

将当前 fragment 与另一个 fragment 比较,看是否相等。。

先比较 fragment 的内容大小,再逐个对内容节点调用节点的 eq 方法进行比较,一旦发现不一样的则返回 false,否则返回 true。
firstChild: Node | null

返回当前 fragment 的第一个子节点,如果是空则为 null

lastChild: Node | null

返回当前 fragment 的最后一个节点,如果是空则为 null

childCount: number

当前 fragment 的子节点数量。

child(indexnumber) → Node

获取 fragment 在给定 index 的子节点。如果 index 超出范围则抛出一个错误。

maybeChild(indexnumber) → Node | null

获取给定 index 的子节点,如果存在的话。

不存在返回 undefined。
forEach()

为每一个子节点调用 f 函数,参数是子节点、子节点相对于当前节点的偏移、以及子节点的 index。

findDiffStart(otherFragment, pos⁠?: number = 0) → number | null

寻找当前 fragment 和给定 fragment 的第一个不同的位置,如果它们相同的话返回 null

findDiffEnd(
pos⁠?: number = this.size,
otherPos⁠?: number = other.size
) → {anumber, bnumber} | null

从后往前搜索,寻找当前 fragment 和给定 fragment 的第一个不同的位置,如果相同则返回 null。 因为该位置在两个节点中可能是不同的,因此该函数返回的是一个对象,带有两个不同的位置。

对象是 {a: number, b: number}。
toString() → string

返回一个用来 debug 的 string 以描述该 fragment。

toJSON() → any

返回该 fragment 序列化后的 JSON 表示。

static fromJSON(schemaSchema, valueany) → Fragment

从其JSON表示中反序列化片段。

static fromArray(arrayreadonly Node[]) → Fragment

用一个节点数组构建一个 fragment。带有相同 marks 的相邻文本节点会被合并到一起。

static from(
nodes⁠?: Fragment | Node | readonly Node[] | null
) → Fragment

用给定的类节点集合的对象中创建一个 fragment。如果是 null 则返回空 fragment。 如果是 fragment 则返回该 fragment 自身。如果是一个节点或者一个节点数组,则返回一个包含这些节点的 fragment。

static empty: Fragment

一个空的 fragment。没有包含任何节点的 fragment 都指向该对象(而不是为每个 fragment 都创建一个空的 fragment)。

class Mark

Mark 是可以被附加到节点上的一小段信息,比如加粗行内代码或者加粗链接字体。它有一个可选的 attributes 集合以提供更多信息 (比如链接的 target 信息等)。Marks 通过 Schema 创建,它控制哪些 marks 存在于哪些节点以及拥有哪些 attributes。

type: MarkType

当前 mark 的 type。

attrs: Attrs

与此 mark 相关的 attributes。

addToSet(setreadonly Mark[]) → readonly Mark[]

将当前 marks 加入到给定 marks 集合的右侧(后面)后返回新的 marks 集合。如果当前 marks 已经存在于给定集合当中 那么给定集合自身会被返回。如果给定集合中有任何 marsk 配置对象的 exclusive 属性值中有当前 mark,那么它会被用当前 mark 替换掉。

removeFromSet(setreadonly Mark[]) → readonly Mark[]

从给定的 marks 集合中移除当前 mark。如果当前 mark 不在集合中,那么给定集合本身会被返回。

isInSet(setreadonly Mark[]) → boolean

测试是否当前 mark 在给定 marks 集合中。

eq(otherMark) → boolean

测试当前 mark 与给定 mark 是否有相同的类型和 attributes。

toJSON() → any

返回当前 mark 的 JSON 序列化的表示。

static fromJSON(schemaSchema, jsonany) → Mark

从 JSON 反序列化标记。

static sameSet(areadonly Mark[], breadonly Mark[]) → boolean

测试两个 marks 集合是否一样。

marks 集合是否相同的比较是是先测试 marks 集合中的 mark 数量,然后逐个调用 mark 的 eq 进行比较。
static setFrom(marks⁠?: Mark | readonly Mark[] | null) → readonly Mark[]

用给定的参数,新建一个 stored marks 集合,该参数可能是 null、单独一个 mark或者一个未排序的 marks 数组。

static none: readonly Mark[]

marks 的空集合。

class Slice

一个 slice 表示从大的文档中切出去的一小段片段。它不仅存储着 fragment,还有它两侧的节点「开放」的深度(切割节点产生的)。

new Slice()

新建一个 slice。当指定一个非零的开放深度的时候,你必须保证该 slice 的 fragment 至少在这个深度存在节点。 例如,如果节点是一个空的段落节点,那么 openStartopenEnd 不能大于 1。

开放节点的内容无需一定要符合 schema 的内容限制,因为对于开放节点来说,它的内容应该被在一个有效的开始/结束/中间位置开放,而这具体取决于节点的哪一侧被打开。

这句话举个例子比较好理解,如果 schema 内容限制 li 不能包含 p,但如果一个 slice 的 fragment 结构是 <li><p>123</p><p>456</p></li>,openStart 是 2,openEnd 也是 2 那么该 slice 切割(打开/开放)出来的节点就会形如 123<p>456, 因此也是一个有效的 slice,可以被放到文档中需要的地方去(如另个一 p 标签内)。
content: Fragment

该 slice 的内容片段。

该内容片段以 Fragment 的实例形式存在。
openStart: number

片段开始时的开放深度。

openEnd: number

结束位置的开放深度。

size: number

当将插入 slice 到文档中时,插入的内容大小。

eq(otherSlice) → boolean

测试当前 slice 是否与另一个 slice 相等。

相等比较是比较 slice 的内容,也即调用 fragment 的 eq 方法比较的,而且需要满足 openStart 相等和 openEnd 相等。
toJSON() → any

返回当前 slice 的 JSON 序列化的表示。

static fromJSON(schemaSchema, jsonany) → Slice

从 slice 的 JSON 表示形式反序列化出一个 slice。

static maxOpen(
openIsolating⁠?: boolean = true
) → Slice

从一个 fragment 新建一个 slice,该 slice 两侧的的打开值尽可能的大。

static empty: Slice

空的 Slice。

type Attrs

一个包含节点属性的对象。

class ReplaceError extends Error

一种调用 Node.replace 方法时如果给定替换内容不可用的话会返回的错误类型。

Resolved Positions

在文档中的位置可以表示为一个整数的 offsets。不过你经常会想要使用一种更方便表达形式来使用位置信息。

class ResolvedPos

你可以 resolve 一个位置以得到该位置的更多信息。该类的对象就是表示这样一种 resolve 过的位置, 它提供一些上下文信息,以及一些有用的工具函数。

通过这个接口,对于那些接受可选参数 depth 的方法来说,如果没有传入该参数则默认会是 this.depth,如果是负数则会是 this.depth + value

depth: number

从根节点开始算,它的父节点的深度。如果位置直接指向根节点,则是 0。如果它指向一个顶级节点如段落,则是 1,以此类推。

pos: number

被 resolve 的位置。

parentOffset: number

该位置相对于父节点的偏移量。

parent: Node

该位置指向的父级节点。记住,即使一个位置指向的是文本节点,那该节点也不认为是父级,因为文本节点在 ProseMirror 世界里是 「扁平」的,它没有内容。

doc: Node

该位置被 resolve 的根节点。

node(depth⁠?: number) → Node

在给定深度的祖先节点。p.node(p.depth)p.parent` 相同。

index(depth⁠?: number) → number

在给定深度的祖先节点的 index。例如,如果该位置指向顶级节点的第二个段落的第三个节点,那么 p.index(0) 是 1,p.index(1) 是 2。

indexAfter(depth⁠?: number) → number

在给定深度的祖先节点后面节点的 index。

start(depth⁠?: number) → number

给定深度的祖先节点的开始位置(绝对位置)。

绝对位置是相对于 doc 根节点的位置,一般都是用它来定位。
end(depth⁠?: number) → number

给定深度的祖先节点的结束位置(绝对位置)。

before(depth⁠?: number) → number

在给定深度的祖先节点之前的(绝对)位置,或者,如果 depththis.depth + 1 的话,则是原始的位置。

after(depth⁠?: number) → number

在给定深度的祖先节点之后的(绝对)位置,或者如果 depththis.depth + 1 的话则是原始的位置。

「before 之前」、「start 开始」、「after 之后」、「end 结束」位置的区别:有以下结构 <p>123</p>,若用 I表示「这个」位置,则: I<p>123</p> 表示 before;<p>I123</p> 表示 start;<p>123I</p> 表示 end;<p>123</p>I 表示 after。
textOffset: number

当位置指向一个文本节点,该函数返回当前位置到文本节点开始位置的距离。如果指向节点之间则是 0。

nodeAfter: Node | null

获取紧挨着该位置后的节点,如果有的话。如果位置指向一个文本节点,则只有在文本节点中该位置之后的内容会被返回。

nodeBefore: Node | null

获取紧挨着该位置前的节点,如果有的话。如果位置指向一个文本节点,则只有在文本节点中该位置之前的内容会被返回。

posAtIndex(indexnumber, depth⁠?: number) → number

获取在给定深度的祖先节点的给定 index 的位置(深度默认是 this.depth)。

marks() → readonly Mark[]

充分考虑 marks 们的 inclusive 属性后,获取当前位置的最终的 marks。如果该位置是在一个非空节点的起始位置,则会返回该位置之后节点的 marks(如果有的话)。

如果位置在一个空元素内,则返回空的数组(即 Mark 的静态属性,Mark.none)。如果是在一个文本节点中,则简单返回文本节点的 marks。 如果在一个非空节点的起始位置(before 为空),则考虑该位置之后节点的 marks。最后(此时只剩一种情况,也即在一个非文本节点的末尾位置)考虑排除那些设置了 inclusive 属性为 false 的 marks 们。
marksAcross($endResolvedPos) → readonly Mark[] | null

获取在当前位置之后的 marks,如果有的话,会排除那些 inclusive 为 false 以及没有出现在 $end 位置的 marks 们。 这个方法最有用的场景是在执行删除操作后获取需要保留的 marks 集合。如果该位置在它的父级节点的结束的地方或者它的父级节点不是一个文本 block,则会返回 null(此时不应该有任何 marks 被保留)。

sharedDepth(posnumber) → number

返回在给定位置和当前位置拥有的相同父级节点所在的最大深度。

blockRange(
other⁠?: ResolvedPos = this,
pred⁠?: fn(nodeNode) → boolean
) → NodeRange | null

根据当前位置与给定位置围绕块级节点的周围看返回相应的 Range。例如,如果两个位置都指向一个文本 block,则文本 block 的 range 会被返回; 如果它们指向不同的块级节点,则包含这些块级节点的深度最大的共同祖先节点 range 将会被返回。你可以传递一个指示函数,来决定该祖先节点是否可接受。

sameParent(otherResolvedPos) → boolean

当前位置和给定位置是否具有相同的父级节点。

max(otherResolvedPos) → ResolvedPos

返回当前位置和给定位置较大的那个。

min(otherResolvedPos) → ResolvedPos

返回当前位置和给定位置较小的那个。

class NodeRange

表示一个内容的扁平范围(range),例如,一个开始和结束在相同节点的范围。

new NodeRange()

构造一个节点 range。至少深度在 depth 及更小的时候 $from$to 应该始终指向相同的节点,因为一个节点 range 表示具有相同父级节点的相邻节点的集合。

$from: ResolvedPos

range 的内容开始处 resolve 过的位置。它可能有一个大于该 range 的 depth 属性的深度,因为这些位置是用来计算 range 的,其不会直接在 range 的边界再次 resolve。

$to: ResolvedPos

range 的内容结束处 resolve 过的位置。看一下关于 $from 的警告。

举个例子:有以下结构 <ul><li><p>abc</p></li><li><p>123</p><p>456</p></li></ul> 则构造一个 NodeRange 的时候,如果 $from 在 1 后面位置,$to 在 4 后面位置,则 depth 必须是在第二个 li 的开始位置的深度或者更小,因为如果再深的话,$from 和 $to 就没有共同的父级节点,就无法构建一个 NodeRange。也因此,$from 和 $to 的 depth 属性是有可能大于 NodeRange 的 depth 属性的。
depth: number

该 range 指向的节点的深度。

start: number

该 range 开始的位置。

end: number

该 range 结束的位置。

parent: Node

该 range 所在的父级节点。

startIndex: number

该 range 在父级节点中的开始处的 index。

endIndex: number

该 range 在父级节点中结束处的 index。

Document Schema

每个 ProseMirror 文档都符合一个 schema 约束,它描述了节点的集合和 marks,以及它们之间的关系,比如哪些节点可以作为其他节点的子节点等。

class Schema<Nodes extends string = any, Marks extends string = any>

一个文档的 schema。对可能出现在文档中的 nodes 和 marks 提供相应的 node typemark type 对象, 以及提供相应创建和序列化这样一个文档的函数。

当给定时,类型参数提供此架构中节点和标记的名称。

new Schema(specSchemaSpec<Nodes, Marks>)

构造一个 schema 从一个 schema specification(配置对象)中。

spec: {
nodesOrderedMap<NodeSpec>,
marksOrderedMap<MarkSpec>,
topNode⁠?: string
}

当前 schema 所基于的 spec(配置对象),其中的 nodesmarks 属性可以保证是 OrderedMap 的实例(不是原始对象)。

nodes: {[name in Nodes]: NodeType} & Object<NodeType>

一个 schema 中节点名和节点类型对象的键值对映射。

marks: {[name in Marks]: MarkType} & Object<MarkType>

一个 mark 名和 mark 类型对象的键值对映射。

linebreakReplacement: NodeType | null

换行符替换节点在此模式中定义(如果有)。

topNodeType: NodeType

当前 schema 的 默认顶级节点 类型。

cached: Object<any>

一个用于计算和缓存每个 schema 中的任何类型值的对象。(如果你想要在其上储存一些东西,要保证属性名不会冲突)

node(
attrs⁠?: Attrs | null = null,
content⁠?: Fragment | Node | readonly Node[],
marks⁠?: readonly Mark[]
) → Node

在 schema 中新建一个节点。type 参数可以是一个字符串或者一个 NodeType 的实例。Attributes 会被以默认值扩展,content 可能是一个 FragmentnullNode 或者一个节点数组。

text(textstring, marks⁠?: readonly Mark[]) → Node

在 schema 中新建一个文本节点。不允许创建空的文本节点。

文本节点和文本块节点不同,注意区分。
mark(typestring | MarkType, attrs⁠?: Attrs) → Mark

用给定的类型和 attributes 创建一个 mark。

nodeFromJSON(jsonany) → Node

从一个 JSON 表达式中反序列化出一个节点。该方法 this 已经绑定当前对象。

JSON 表达式其实并不是 JavaScript 中通常意义上的 JSON 字符串,而是一个普通对象,它及它的键值都是 plain object。该对象由相应的 Node.toJSON 生成。
markFromJSON(jsonany) → Mark

从一个 JSON 表达式中反序列化出一个 mark。该方法 this 已经绑定当前对象。

该对象由相应的 Mark.toJSON 生成。

interface SchemaSpec<Nodes extends string = any, Marks extends string = any>

一个描述 schema 的对象,用来传递给 Schema 构造函数

就是 schema 的配置对象,ProseMirror 中的 xxxSpec 都是 xxx 的配置对象,如 NodeSpec、MarkSpec 等。
nodes: {[name in Nodes]: NodeSpec} | OrderedMap<NodeSpec>

当前 schema 中所有的 node 类型的对象。对象中,键是节点名,对象的键是对应的 NodeSpec。 节点们在该对象中出现的先后顺序是非常重要的,它决定了默认情况下哪个节点的 parse rules 优先进行, 以及哪个节点是一个 group 优先考虑的节点。

marks⁠?: {[name in Marks]: MarkSpec} | OrderedMap<MarkSpec>

当前 schema 中的所有 mark 类型的对象。它们出现的顺序决定了在 mark sets 中的存储顺序,以及 parse rules 的处理顺序。

topNode⁠?: string

当前 schema 顶级节点的名字,默认是 "doc"

interface NodeSpec

节点类型的描述,用于定义模式时。

content⁠?: string

就像在 schema guide 中描述的一样,为当前节点的内容表达式。 如果没有给定,则该节点不允许任何内容。

schema guide 链接指向中文翻译指南,请搜索 Schema 下的 Content Expressions 一节。
marks⁠?: string

当前节点允许的 marks 类型。可能是一个空格分隔的字符串,内容是 mark 的名字或者 group 名。 "_" 表示明确允许所有的 marks,或者 "" 表示禁止所有的 marks。如果没有设置该字段,则节点含有的内联内容将会默认允许所有的 marks, 其他不含内联内容的节点将默认不允许所有的 marks。

group⁠?: string

当前节点所属的 group,可以出现多个,用空格分隔,可以指向当前 schema 的内容表达式(content expressions)。

inline⁠?: boolean

对于内联节点,应该被设置为 true(文本节点隐式的被设置为 true)。

atom⁠?: boolean

可以被设置为 true,以表示即使当前节点不是一个 leaf node,但是其也没有直接可编辑内容, 因此在 view 中应该被当成是一个独立的单位对待。

「独立单位对待」指的是,如在计数上,应该是 1;在事件上,内部元素触发的事件应该被视作是该节点触发的,等。
attrs⁠?: Object<AttributeSpec>

当前节点拿到的 attributes。

selectable⁠?: boolean

控制当前类型的节点是否能够被作为 node selection 所选中。 对于非文本节点来说,默认是 true。

draggable⁠?: boolean

决定在未选中的情况下,当前类型的节点能否被拖拽。默认是 false。

code⁠?: boolean

指示当前节点包含 code,其会引起一些命令有特别的行为。

「特别的行为」如,在 code 节点中的内容如果是 li 和 文档中的 li 是两个处理逻辑,前者针对 code 块处理;后者针对 li 进行处理。
whitespace⁠?: "pre" | "normal"

控制此节点中的空白符解析方式。默认值是"normal",这会导致DOM 解析器在正常模式下折叠空白符,并在其他情况下将其规范化(用空格替换换行符等)。"pre"会使解析器保留节点内的空白符。当未提供此选项但code为 true 时,whitespace将默认为"pre"。请注意,此选项不会影响节点的渲染方式——这应由toDOM和/或样式处理。

definingAsContext⁠?: boolean

确定在替换操作(例如粘贴)期间此节点是否被视为重要的父节点。非定义(默认)节点在其全部内容被替换时会被删除,而定义节点则会保留并包裹插入的内容。

最后一句话讲的是,例如,默认的 paragraph 中,文本块节点,粘贴的时候应该直接替换掉它的父节点,也即另一个文本块。 但是对非默认 paragraph(即你自己定制的 paragraph)的话,在替换内容的时候,就需要保留该 非默认 paragraph 的一些属性,不能直接替换。同理 li 元素, 因为首先选中 li 元素内容,然后粘贴内容是一个很常见的操作,用户的预期是将粘贴内容作为 li 的内容,而不是直接替换掉 li 而粘贴成 paragraph(或其他 block)。
definingForContent⁠?: boolean

在插入的内容中,内容的定义父级在可能的情况下会被保留。通常,非默认段落文本块类型,可能还有列表项,会被标记为定义。

defining⁠?: boolean

启用后,启用 definingAsContextdefiningForContent

isolating⁠?: boolean

当该属性设置为 true 时(默认是 false),当前类型的节点的两侧将会计算作为边界,于是对于正常的编辑操作如删除、或者提升,将不会被跨越过去。 举个例子,对于 table 的 cell 节点,该属性应该被设置为 true。

「提升」操作指的是,如在一个二级 li 中,一般用户习惯下,按 shift + tab 会将该二级 li 提升到一级 li。
toDOM⁠?: fn(nodeNode) → DOMOutputSpec

定义当前节点的默认序列化成 DOM/HTML 的方式(被DOMSerializer.fromSchema使用)。 应该返回一个 DOM 节点或者一个描述 ODM 节点的 array structure,它带有可选的数字 0 (就是「洞」), 表示节点的内容应该被插在哪个位置。

对于文本节点,默认是创建一个文本 DOM 节点。虽然创建序列化器以将文本节点特殊渲染是可能的,但是当前编辑器并不支持这样做,因此你不应该覆盖文本节点中的该方法。

parseDOM⁠?: readonly TagParseRule[]

当前节点相关的 DOM parser 信息,会被 DOMParser.fromSchema 使用以自动的衍生出一个 parser。Rule 中的 node 字段是隐式的(节点的名字会自动填充)。如果你在此处提供了自己的 parser,那你就不需要再在 schema 配置的时候提供 parser 了。

配置 Editor view 的时候可以配置一个 Parser 和 Serializer,如果提供,则此处就不用写 parseDOM 了。
toDebugString⁠?: fn(nodeNode) → string

定义一个该类型节点被序列化成一个字符串形式的默认方法,以做 debugging 用途。

leafText⁠?: fn(nodeNode) → string

定义了这种类型的叶节点序列化为字符串的默认方式(由Node.textBetweenNode.textContent使用)。

linebreakReplacement⁠?: boolean

在模式中,单个内联节点可以设置为换行符等效项。在支持该节点的块类型和不支持但具有whitespace设置为"pre"的块类型之间进行转换时,setBlockType将适当地在换行符和换行节点之间进行转换。

[string]: any

节点规格可能包括任意属性,这些属性可以通过NodeType.spec被其他代码读取。

interface MarkSpec

用于在创建模式时定义标记。

attrs⁠?: Object<AttributeSpec>

当前 mark 类型拿到的 attributes。

inclusive⁠?: boolean

当光标放到该 mark 的结尾处(或者如果该 mark 开始处同样是父级节点的开始处时,放到 mark 的开始处)时,该 marks 是否应该被激活。默认是 true/

「被激活」的意思是,可以通过 API 获取光标所在的 resolvedPos 信息以查到相关的 marks,对用户来说被激活意味着在该地方输入内容会带上相应的 marks。
excludes⁠?: string

决定当前 mark 是否能和其他 marks 共存。应该是由其他 marks 名或者 marks group 组成的以空格分隔的字符串。 当一个 marks 被 added 到一个集合中时,所有的与此 marks 排斥(excludes)的 marks 将会被在添加过程中移除。 如果当前集合包含任何排斥当前的新 mark 的 mark,但是该新 mark 却不排斥它,则当前新的 mark 不会被添加到集合中。你可以使用 "_" 来表明当前 marks 排斥所有的 schema 中的其他 marks。

该段的主要意思是,第一:假设 A 、B 互斥,则 无论 A 添加到包含 B 的集合,还是 B 添加到 包含 A 的集合,已经在集合中的一方会被移除以添加新的 mark; 第二:若假设 A 排斥 B,B 却不排斥 A,则将 B 添加到包含 A 的集合中去的时候,将不会被添加进去。

默认是相同类型的 marks 会互斥。你可以将其设置为一个空的字符串(或者任何不包含 mark 自身名字的字符串) 以允许给定相同类型的多个 marks 共存(之哟啊他们有不同的 attributes)。

group⁠?: string

当前 mark 所属的 一个 group 或者空格分隔的多个 groups。

spanning⁠?: boolean

决定当序列化为 DOM/HTML 的时候,当前类型的 marks 能否应用到相邻的多个节点上去。默认是 true。

toDOM⁠?: fn(markMark, inlineboolean) → DOMOutputSpec

定义当前类型的 marks 序列化为 DOM/HTML 的默认方式。如果结果配置对象包含一个「洞」,则洞的位置就是 mark 内容所在的位置。否则,它会被附加到顶级节点之后。

「否则,它会被附加到顶级节点之后」字面意思吗?有待实验,本人貌似没有印象了。
parseDOM⁠?: readonly ParseRule[]

当前 mark 的相关的 DOM parser 信息(具体请查看相应的 node spec field)。 在 Rules 中的 mark 字段是隐式的。

[string]: any

Mark 规格可以包括其他属性,这些属性可以在使用标记时通过 MarkType.spec 进行检查。

interface AttributeSpec

用来 define node 或者 marks 的 attributes。

default⁠?: any

该 attribute 的默认值,当没有显式提供值的时候使用。如果 attributes 没有默认值,则必须在新建一个 node 或者 mark 的时候提供值。

validate⁠?: string | fn(valueany)

用于验证此属性值的函数或类型名称。这将在从 JSON 反序列化属性时使用,并在运行 Node.check 时使用。当是一个函数时,如果值不是预期的类型或形状,则应引发异常。当是一个字符串时,它应该是一个由原始类型("number""string""boolean""null""undefined")组成的用 | 分隔的字符串,并且当值不是这些类型之一时,库将引发错误。

class NodeType

每个 Node Type 只会被 Schema 初始化一次,然后使用它来tag(归类) Node 的实例。 这种对象包含了节点的类型信息,比如名称以及它表示那种节点。

name: string

该节点类型在 schema 中的名称。

schema: Schema

一个指向节点类型所属 Schema 的指针。

spec: NodeSpec

当前类型的配置对象。

inlineContent: boolean

如果当前节点类型有内联内容的话,即为 true。

isBlock: boolean

当前节点是块级类型的话,即为 true。

判断是否是块级类型是用排除法,如果不是内联类型(即 spec.inline 是 false)且节点类型的名称不是「text」,则该类型是块级类型。
isText: boolean

如果是文本类型的节点,即为 true。

也即节点名字是「text」。
isInline: boolean

如果是一个内联类型,则为 true。

同样使用排除法,即与 spec.isBlock 互斥。
isTextblock: boolean

如果节点是文本块类型节点则为 true,即一个包含内联内容的块级类型节点。

一个块级类型可能包含另一个块级类型,一个文本块类型则只会包含内联内容,哪些节点是内联元素由 schema 决定。
isLeaf: boolean

如果节点不允许内容,则为 true。

是否是叶节点使用的是 spec.contentMatch 是否为空判断的。
isAtom: boolean

如果节点是一个原子节点则为 true,例如,一个没有直接可编辑的内容的节点。

isInGroup(groupstring) → boolean

当此节点类型是给定的的一部分时返回 true。

contentMatch: ContentMatch

节点类型内容表达式的起始匹配。

sorry,这个 contentMatch 我用的比较少,所以也不知道是什么意思,貌似源码内部使用的比较多。
markSet: readonly MarkType[] | null

该节点中允许的标记集。null表示允许所有标记。

whitespace: "pre" | "normal"

节点类型的空白选项。

hasRequiredAttrs() → boolean

告诉你该节点类型是否有任何必须的 attributes。

compatibleContent(otherNodeType) → boolean

指示此节点是否允许与给定节点类型相同的一些内容。

create(
attrs⁠?: Attrs | null = null,
content⁠?: Fragment | Node | readonly Node[] | null,
marks⁠?: readonly Mark[]
) → Node

新建一个此种类型的节点。将会检查给定的 attributes,未给定的话即为默认值(如果该中类型的节点没有任何必须的 attributes,你可以直接传递 null 来使用全部 attributes 的默认值)。 content 可能是一个 Fragment、一个节点、一个节点数组或者 nullmarks 参数与之类似,默认是 null,表示空的 marks 集合。

createChecked(
attrs⁠?: Attrs | null = null,
content⁠?: Fragment | Node | readonly Node[] | null,
marks⁠?: readonly Mark[]
) → Node

create 类似,但是会检查给定的 content 是否符合节点类型的内容限制,如果不符的话会抛出一个错误。

该自定义错误类型为 RangeError。
createAndFill(
attrs⁠?: Attrs | null = null,
content⁠?: Fragment | Node | readonly Node[] | null,
marks⁠?: readonly Mark[]
) → Node | null

create 类似,不过该方法会查看是否有必要在给定的 fragment 开始和结尾的地方 添加一些节点,以让该 fragment 适应当前 node。如果没有找到合适的包裹节点,则返回 null。 记住,如果你传递 null 或者 Fragment.empty 作为内容会导致其一定会适合当前 node,因此该方法一定会成功。

因为 null 和 Fragment.empty 不用寻找任何「合适的包裹节点」就能适应当前节点。
validContent(contentFragment) → boolean

如果给定的片段是此节点类型的有效内容,则返回 true

allowsMarkType(markTypeMarkType) → boolean

检查当前节点类型是否允许给定的 mark 类型。

allowsMarks(marksreadonly Mark[]) → boolean

检查当前节点类型是否允许给定的 marks 集合。

allowedMarks(marksreadonly Mark[]) → readonly Mark[]

从给定的 marks 集合中移除不允许出现在当前 node 中的 marks。

class MarkType

和 nodes 类似,marks(与 node 关联的以表示诸如强调、链接等的内容)也被用类型对象进行 tagged(归类), 每个类型只会被 Schema 实例化一次。

name: string

mark 类型的名称。

schema: Schema

当前 mark 类型所属于的 schema。

spec: MarkSpec

当前 mark 类型的配置对象。

create(attrs⁠?: Attrs | null = null) → Mark

创建一个当前类型的 mark。attrs 可能是 null 或者是一个仅包含部分 marks attributes 的对象。 其他未包含的 attributes,会使用它们的默认值添加上去。

removeFromSet(setreadonly Mark[]) → readonly Mark[]

如果当前 mark 类型存在与给定的 mark 集合,则将会返回不含有当前 mark 类型的 marks 集合。 否则,直接返回给定的 marks 集合。

看函数名,顾名思义就是在给定 marks 集合中移除当前 mark 类型的 marks。
isInSet(setreadonly Mark[]) → Mark | undefined

检查当前类型的 marks 是否存在于给定 marks 集合。

excludes(otherMarkType) → boolean

查询给定的 mark 类型是否与当前 mark 类型 excluded(互斥)

class ContentMatch

该类的实例表示一个节点类型的 content expression(内容表达式) 的匹配状态, 其可以用来寻找是否此处是否有更进一步的内容能够匹配到,以及判断一个位置是否是该节点的可用的结尾。

本小节的方法和类我用的不多,可能在处理一些边缘 case 的情况才会用到,因此很多直译了。
validEnd: boolean

当匹配状态表示该节点有一个可用的结尾时为 true。

matchType(typeNodeType) → ContentMatch | null

匹配一个节点类型,如果成功则返回该 ContentMatch 匹配结果。

matchFragment(
start⁠?: number = 0,
end⁠?: number = frag.childCount
) → ContentMatch | null

尝试去匹配一个 fragment。如果成功则返回 ContentMatch 匹配结果。

defaultType: NodeType | null

获取相应匹配位置的第一个可以被生成的匹配节点类型。

「可以被生成的」指的是该位置不能是文本节点或者不能有必须存在的 attribute 才能被生成。
fillBefore(
toEnd⁠?: boolean = false,
startIndex⁠?: number = 0
) → Fragment | null

尝试匹配给定的 fragment,如果失败,则会查看是否可以通过在该 fragment 前面插入一些节点来使之匹配。 如果插入节点后匹配成功,则会返回一个插入的节点组成的 fragment(如果没有需要插入的节点,则可能是空的)。 当 toEnd 为 true 时,只有结果匹配到达了内容表达式的结尾之时,才会返回一个 fragment。

否则返回 undefined。
findWrapping(targetNodeType) → readonly NodeType[] | null

寻找一个包裹给定节点的节点集合,该集合中的节点在包裹住给定类型的节点后才能出现在当前位置。 集合的内容可能是空(如果给定类型节点直接就适合当前位置而无需包裹任何节点时),若不存在相应的包裹节点,则集合也可能是 null。

edgeCount: number

在描述内容表达式的有限自动机中该节点拥有的外部边界的数量。

没理解什么意思,需要看源码,我直译的,鼠标悬浮查看原始文档。
edge(nnumber) → {typeNodeType, nextContentMatch}

在描述内容表达式的有限自动机中获取该节点第 n 个外部的边界。

DOM Representation

由于用一颗 DOM 节点树来表示一个文档是 ProseMirror 进行各种操作的核心思想,因此 DOM parsingserializing 被集成进该模块中。

(不过记住,你 不需要 使用该模块来实现一个 DOM 操作接口。)

class DOMParser

一个为了让 ProseMirror 文档符合给定 schema 的 Parser。它的行为由一个 rules 数组定义。

new DOMParser(
rulesreadonly ParseRule[]
)

新建一个针对给定 schema 的 parser,使用给定的 parsing rules。

schema: Schema

parser 所 parses 的 schema。

解析器所解析的 schema。
rules: readonly ParseRule[]

parser 所使用的 parse rules,按顺序优先。

parse(domDOMNode, options⁠?: ParseOptions = {}) → Node

parse 一个 DOM 节点的内容成一个文档。

parseSlice(domDOMNode, options⁠?: ParseOptions = {}) → Slice

parses 给定的 DOM 节点,与 parse 类似,接受与之相同的参数。 不过与 parse 方法产生一整个节点不同的是,这个方法返回一个在节点两侧打开的 slice,这意味着 schema 的约束不适用于输入节点左侧节点的开始位置和末尾节点的结束位置。

这表示该方法可能产生一个不受 schema 约束的 node,只是该 node 由于 openStart 和 openEnd 的存在而适合 schema (被 open 剪切掉以适合 schema,但是整体不适合 schema)。
static fromSchema(schemaSchema) → DOMParser

用给定的 schema 中的 node 配置对象 中的 parsing rule 来构造一个 DOM parser, 被按 优先级 重新排序。

interface ParseOptions

这是一个被 parseparseSlice 方法用到的参数配置对象。

preserveWhitespace⁠?: boolean | "full"

默认情况下,根据 HTML 的规则,空白符会被折叠起来不显示。传递 true 表示保留空白符,但会将换行符表示为空格。 "full" 表示完全保留所有的空白符。

findPositions⁠?: {nodeDOMNode, offsetnumber, pos⁠?: number}[]

如果设置了该参数,则 parser 除了 parsing 内容外,还将记录给定位置 DOM 在文档中相应的位置。 它将通过写入对象,添加一个保存文档位置的 pos 属性来实现。不在 parsed 内容中的 DOM 的位置将不会被写入。

from⁠?: number

从开始 parsing 位置计算的子节点的索引。

to⁠?: number

从结束 parsing 位置计算的子节点的索引。

topNode⁠?: Node

默认情况下,内容会被 parsed 到 schema 的默认 顶级节点 中。 你可以传递这个选项和 attributes 以使用一个不同的节点作为顶级容器。

topMatch⁠?: ContentMatch

提供与 parsed 到顶级节点的内容匹配的起始内容匹配。

context⁠?: ResolvedPos

在 parsing 的时候的一个额外的节点集合,其被算作给定 top node 之上的 context

interface GenericParseRule

标签样式解析规则中可能存在的字段。

priority⁠?: number

可以使用它来提升 schema 中 parse rule 的优先级顺序。更高优先级的更先被 parse。 没有优先级设置的 rule 则被默认设置一个 50 的优先级。该属性只在 schema 中才有意义。 而在直接构造一个 parser 的时候使用的是 rule 数组的顺序。

consuming⁠?: boolean

默认情况下,如果一个 rule 匹配了一个元素或者样式,那么就不会进一步的匹配接下来的 rule 了。 而通过设置该参数为 false,你可以决定即使当一个 rule 匹配了,在该 rule 之后的 rule 也依然会运行一次。

context⁠?: string

如果设置了该属性,则限制 rule 只匹配给定的上下文表达式,该上下文即为被 parsed 的内容所在的父级节点。 应该包含一个或者多个节点名或者节点 group 名,用一个或者两个斜杠结尾。例如 "paragraph/" 表示只有当父级节点是段落的时候才会被匹配, "blockquote/paragraph/" 限制只有在一个 blockquote 中的一个段落中才会被匹配,"section//" 表示匹配在一个 section 中的任何位置--一个双斜线表示匹配 任何祖先节点序列。为了允许多个不同的上下文,它们可以用 | 分隔,比如 "blockquote/|list_item/"

mark⁠?: string

包裹匹配内容的 mark 类型的名字。

ignore⁠?: boolean

如果是 true,则当前 rule 的内容会被忽略。

closeParent⁠?: boolean

如果是 true,则会在寻找匹配该 rule 的元素的时候关闭当前节点。

skip⁠?: boolean

如果是 true,则会忽略匹配当前规则的节点,但是会 parse 它的内容。

attrs⁠?: Attrs

由该 rule 创建的节点或者 mark 的 attributes。如果 getAttrs 存在的话,getAttrs 优先。

interface TagParseRule extends GenericParseRule

解析规则定位一个DOM元素。

tag: string

描述要匹配的DOM元素类型的CSS选择器。

namespace⁠?: string

要匹配的命名空间。仅当命名空间匹配或此属性为 null 时才匹配节点。

node⁠?: string

匹配此规则时要创建的节点类型的名称。每个规则应具有nodemarkignore属性(除非它出现在nodemark spec中,在这种情况下,nodemark属性将从其位置派生)。

getAttrs⁠?: fn(nodeHTMLElement) → false | Attrs | null

用来计算由当前 rule 新建的节点或者 mark 的 attributes。也可以用来描述进一步 DOM 元素或者行内样式匹配的话需要满足的条件。 当它返回 false,则 rule 不会匹配。当它返回 null 或者 undefined,则被当成是一个空的/默认的 attributes 集合。

contentElement⁠?: string |

对于生成非叶节点的规则,默认情况下,DOM 元素的内容会被解析为节点的内容。如果子节点位于后代节点中,这可能是解析器必须使用的 CSS 选择器字符串,以找到实际的内容元素,或者是一个返回实际内容元素给解析器的函数。

getContent⁠?: fn(nodeDOMNode, schemaSchema) → Fragment

如果设置了该方法,则会使用函数返回的结果来作为匹配节点的内容,而不是 parsing 节点的子节点。

preserveWhitespace⁠?: boolean | "full"

控制当 parsing 匹配元素的内容的时候,空白符是否应该保留。false 表示空白符应该不显示, true 表示空白符应该不显示但是换行符会被换成空格,"full" 表示换行符也应该被保留。

interface StyleParseRule extends GenericParseRule

一个解析规则,目标是一个样式属性。

style: string

一个要匹配的CSS属性名称。此规则将匹配列出该属性的内联样式。也可以采用"property=value"的形式,在这种情况下,只有当属性的值完全匹配给定值时,规则才会匹配。(对于更复杂的过滤器,请使用getAttrs并返回false以指示匹配失败。)匹配样式的规则只能生成marks,而不是节点。

clearMark⁠?: fn(markMark) → boolean

样式规则可以从活动标记集中移除标记。

getAttrs⁠?: fn(nodestring) → false | Attrs | null

用于计算由此规则创建的节点或标记属性的函数。使用样式的值调用。

type ParseRule = TagParseRule | StyleParseRule

一个描述了如何 parse 给定 DOM 节点及行内样式成 ProseMirror 节点及 mark 的对象。

class DOMSerializer

一个 DOM serializer 知道如何将不同类型的 ProseMirror 节点和 marks 转换成 DOM 节点。

new DOMSerializer()

新建一个 serializer。nodes 应该是一个对象,键是节点名,值是一个函数,函数接受一个节点作为参数,返回相应的 DOM 描述。 marks 类似,键值 marks 名,值是一个函数,只不过函数参数表示的是 marks 的内容是否是 block 的或者是 inline 的(一般情况下应该是 inline 的)。 一个 mark serializer 可能是 null,表示这种类型的 mark 不应该被 serialized(序列化)。

nodes: Object<fn(nodeNode) → DOMOutputSpec>

节点的 serialization 函数们。

marks: Object<>

mark 的 serialization 函数们。

serializeFragment(
options⁠?: {document⁠?: Document} = {},
) → HTMLElement | DocumentFragment

将给定的 fragment serialize 成 DOM fragment。如果该操作不是在浏览器中完成, 那么应该传递一个 document 参数,以让 serializer 能够新建 nodes 们。

即 option.document,如果没有传,默认使用的是 window.document。
serializeNode(
options⁠?: {document⁠?: Document} = {}
) → DOMNode

将节点 serialize 成一个 DOM 节点。这对于当想要 serialize 文档的一部分而不是整个文档的时候很有用。 若要 serialize 整个文档,在它的 content 属性上调用 serializeFragment 来完成。

static renderSpec() → {domDOMNode, contentDOM⁠?: HTMLElement}

渲染一个 output 配置对象 到一个 DOM 节点。如果配置对象有一个洞(数字0), 则 contentDOM 将会指向该洞所代表的节点。

static fromSchema(schemaSchema) → DOMSerializer

使用 schema 中节点和 mark 配置对象的 toDOM 方法来构建一个 serializer。

static nodesFromSchema(schemaSchema) → Object<fn(nodeNode) → DOMOutputSpec>

将模式的节点规范中的序列化器收集到一个对象中。 这可以作为构建自定义序列化器的基础。

static marksFromSchema(schemaSchema) → Object<>

将模式的标记规范中的序列化器收集到一个对象中。

type DOMOutputSpec = string |
{domDOMNode, contentDOM⁠?: HTMLElement} |
[string, any]

对DOM结构的描述。可以是一个字符串,它被解释为一个文本节点,一个DOM节点,它被解释为自身,一个{dom, contentDOM}对象,或一个数组。

这个数组描述了一个 DOM 元素。数组中的第一个值应该是这个 DOM 元素名字字符串,可以允许带上命名空间 URL 的前缀或者空格。 如果数组第二个值是一个普通对象,则被当做是 DOM 元素的 attributes。在数组第二个值之后的任何值(包括第二个值,如果它不是一个普通属性对象的话) 都被认为是该 DOM 元素的子元素,因此这些后面的值必须是有一个有效的 DOMOutputSpec 值,或者是数字 0。

数字 0(念做「洞」)被用来指示子元素应该被放置的位置。如果子元素是一个会被放置内容的节点,那么 0 应该是它唯一子元素。

举个例子: ['div', {style:'color:red'}, 0],表示的是 <div style="color:red">子元素<div>; ['div', {style:'color:red'}, ['p', 0]],表示的是 <div style="color:red"><p>子元素</p><div>; ['div', {style:'color:red'}, ['p'], 0] 非法,因为 0 作为一个放置子元素的容器,其并不是父节点 div 的唯一子元素,父节点还有个子元素是 p。

prosemirror-transform module

这个模块定义了一种修改文档的方式,以允许修改被记录、回放、重新排序。你可以在 中文指南 了解更多。

Steps

Transforming 发生在一个或者多个 Step 中,step 是原子的及定义良好的一个修改文档的类。 Applying(应用) 一个 step 会产生一个新的文档。

每个 step 提供一个 change map(修改映射),它会在旧的文档和 transformed 后的文档之间映射。 Steps 可以被 inverted(反转) 以新建一个 step 来取消之前 step 所做的影响, 而且可以在一个叫做 Transform 的对象上方便的链式调用。

abstract class Step

一个 step 对象表示对文档的一个原子修改。大体上讲,它只会应用到创建它的那个文档上去,因为其内的位置信息只有对那个文档来说才有意义。

新的 steps 通过创建扩展自 Step 的类来定义,其覆盖了 applyinvertmapgetMapfromJSON 方法, 此外注册类的时候还需要使用 Step.jsonID 来生成一个唯一的 JSON 序列化过的标识符。

abstract apply(docNode) → StepResult

将当前 step 应用到给定的文档,返回一个结果对象,对象可能表示失败,如果 step 不能被应用到文档中; 也可能表示成功,此时它会包含一个转换后的文档。

getMap() → StepMap

获取由当前 step 产生的表示文档变化的 step map,可以用来在新旧两个文档之间转换位置。

abstract invert(docNode) → Step

新建一个当前 step 相反的 step 版本,需要 step 之前的文档作为参数。

abstract map(mappingMappable) → Step | null

通过一个可 mappable 的东西来 map 当前 step,返回值可能是一个调整过位置的 step 版本, 或者 null,如果 step 完全被这个 mapping 删除的话。

merge(otherStep) → Step | null

试着合并当前 step 与给定的 step,会被直接应用到当前 step 之后。如果可能的话,会返回合并之后的 step, 如果 step 不能被合并,则返回 null。

abstract toJSON() → any

新建一个当前 step JSON 序列化后的版本。如果为一个自定义的子类定义了该方法,则需要确保返回的结果对象的 stepType 属性值是 step 类型的 JSON id

static fromJSON(schemaSchema, jsonany) → Step

从一个 step 的 JSON 形式反序列化为一个 step。将会调用 step 类自己实现的此方法。

static jsonID(
stepClass: {fromJSONfn(schemaSchema, jsonany) → Step}
) → {fromJSONfn(schemaSchema, jsonany) → Step}

为了能够将 steps 序列化为 JSON 形式,每个 step 都需要一个字符串 ID 附加到它自己的 JSON 形式上去。 使用这个方法为你的 step 类注册一个 ID。需要避免与其他模块的 step 的命名冲突。

class StepResult

applying(应用) 一个 step 的结果。可能包含一个新的文档或者是一个失败的值。

doc: Node | null

如果成功,转换后的文档。

failed: string | null

失败消息,如果不成功。

static ok(docNode) → StepResult

创建一个成功的 step 结果。

static fail(messagestring) → StepResult

创建一个失败的 step 结果。

static fromReplace(
docNode,
) → StepResult

用给定的参数调用 Node.replace。如果成功就返回一个成功值, 如果它抛出一个 ReplaceError 则返回一个失败值。

class ReplaceStep extends Step

用含有新内容的 slice 来替换文档的一部分。

new ReplaceStep(
structure⁠?: boolean = false
)

给定的 slice 应该适应这个介于 fromto 之间的 「gap」,即 slice 两侧的深度和各自所接起来的位置深度必须是相同的,且 slice 周围 的节点必须能够接起来。当 structure 为 true 的时候,如果介意 from 和 to 之间的内容不是连续的先闭合后开放的标签,则 step 将会失败(这是为了 保证避免 rebased 的 replace step 意外覆盖了一些东西)。

from: number

替换范围的起始位置。

to: number

替换范围的结束位置。

slice: Slice

要插入的切片。

class ReplaceAroundStep extends Step

用一个 slice 的内容替换文档的一部分,不过会通过将被替换内容移动到 slice 中的方式来保留它的一个 range。

new ReplaceAroundStep(
structure⁠?: boolean = false
)

用给定的 range 和 gap 来新建一个 replace-around step。 insert 应该指向的是在 slice 中 gap 的内容应该被放置的位置。 structure 有与它在 ReplaceStep 类中相同的含义。

from: number

替换范围的起始位置。

to: number

替换范围的结束位置。

gapFrom: number

保存范围的开始。

gapTo: number

保存范围的结束。

slice: Slice

要插入的切片。

insert: number

在切片中应插入保留范围的位置。

class AddMarkStep extends Step

在给定的两个位置中的所有内联元素上添加一个 mark。

new AddMarkStep(fromnumber, tonumber, markMark)

创建标记步骤。

from: number

标记范围的开始。

to: number

标记范围的结束。

mark: Mark

添加标记。

class RemoveMarkStep extends Step

在给定的两个位置中的所有内联元素上移除一个 mark。

new RemoveMarkStep(fromnumber, tonumber, markMark)

创建一个去除标记的步骤。

from: number

未标记范围的开始。

to: number

未标记范围的结束。

mark: Mark

要删除的标记。

class AddNodeMarkStep extends Step

在特定节点添加标记。

new AddNodeMarkStep(posnumber, markMark)

创建一个节点标记步骤。

pos: number

目标节点的位置。

mark: Mark

添加标记。

class RemoveNodeMarkStep extends Step

从特定节点移除标记。

new RemoveNodeMarkStep(posnumber, markMark)

创建一个去除标记的步骤。

pos: number

目标节点的位置。

mark: Mark

要删除的标记。

class AttrStep extends Step

更新特定节点中的属性。

new AttrStep(posnumber, attrstring, valueany)

构建属性步骤。

pos: number

目标节点的位置。

attr: string

要设置的属性。

value: any
static fromJSON(schemaSchema, jsonany) → AttrStep

class DocAttrStep extends Step

更新文档节点中的属性。

new DocAttrStep(attrstring, valueany)

构建属性步骤。

attr: string

要设置的属性。

value: any
static fromJSON(schemaSchema, jsonany) → DocAttrStep

Position Mapping

通过调用由 step 产生的 step maps 来从一个文档中映射位置到另一个文档中在 ProseMirror 中是一个非常重要的操作。 例如,它被用来当文档改变的时候更新选区。

interface Mappable

位置可以被好几个对象 map,这类对象都符合该接口。

map(posnumber, assoc⁠?: number) → number

通过该对象 map 一个位置。如果给定该方法,则 assoc(应该是 -1 或者 1,默认是 1) 决定位置与哪一侧有关,这决定了当一块内容被插入到被 map 的位置的时候,该位置应该往哪个方向移动。

mapResult(posnumber, assoc⁠?: number) → MapResult

map 一个位置,然后返回一个包含关于这个 mapping 附加信息的对象。结果的 deleted 字段会告诉你该位置在 map 期间是否被删除(在一个 replace 的 range 中完全闭合的位置,即两侧都删除),如果只有一侧被删除,则只有当 assoc 指向删除一侧的时候,这个位置才会被认为是删除了。

class MapResult

一个带有额外信息的表示一个 map 过的位置的对象。

pos: number

该位置 map 过的版本。

deleted: boolean

告诉您该位置是否已删除,即该 step 是否从文档中删除了通过assoc参数查询的一侧的标记。

deletedBefore: boolean

告诉您映射位置之前的标记是否被删除。

deletedAfter: boolean

当映射位置后的标记被删除时为真。

deletedAcross: boolean

告知是否有任何步骤通过删除跨越位置(包括位置前后的标记)。

class StepMap implements Mappable

一个 map 描述了由 step 产生的删除和插入操作,这可以用来找到应用 step 之前文档的位置和应用 step 之后文档的相同位置之间的对应关系。

new StepMap(
rangesreadonly number[],
inverted⁠?: boolean = false
)

新建一个位置 map。对文档的修改被表示为一个数字数组,在数组中每三个值表示一个修改区域,即 [开始,旧大小,新大小]

forEach()

对该 map 中的每一个修改的 range 调用给定的函数。

invert() → StepMap

新建一个该 map 的反转版本。函数返回的结果可以被用来将 step 修改后的文档位置 map 回 step 修改前的文档位置。

static offset(nnumber) → StepMap

新建一个将所有位置偏移 n (n 可能为负数)的一个 map。当将一个子文档的 step 应用于一个较大文档的时候,这可能会很有用,反之亦然。

static empty: StepMap

一个不包含更改范围的StepMap。

class Mapping implements Mappable

一个 mapping 表示 0 个或者更多个 step maps 的管道。为了能够无损的处理通过一系列 step 而产生的位置 mapping, 其中一些 steps 很有可能是之前 step 的反转版本(这可能出现在为了协同编辑或者历史管理而 ‘rebasing’ step 的时候)因此有一些特殊的规定需要遵守。

new Mapping(
maps⁠?: StepMap[] = [],
mirror⁠?: number[],
from⁠?: number = 0,
to⁠?: number = maps.length
)

用给定的位置 maps 新建一个 mapping。

maps: StepMap[]

在当前 mapping 中的 step maps。

from: number

maps 数组中的起始位置,当 map 或者 mapResult 调用的时候会被使用。

to: number

maps 位置的结束位置。

slice(
from⁠?: number = 0,
to⁠?: number = this.maps.length
) → Mapping

新建一个 mapping,其只 map 当前 mapping 的一部分。

appendMap(mapStepMap, mirrors⁠?: number)

添加一个 step map 到当前 mapping 的末尾。如果设置了 mirrors 参数,则它应该是 step map 的索引,即第一个参数 step map 的镜像。

appendMapping(mappingMapping)

将给定 mapping 的所有 maps 添加到当前 mapping(保留镜像信息)。

getMirror(nnumber) → number | undefined

寻找给定偏移量位置的 map 的镜像 step map 的偏移量。

appendMappingInverted(mappingMapping)

将给定 mapping 的相反顺序的 mapping 附加到当前 mapping 上。

invert() → Mapping

新建一个当前 mapping 包含相反 map 顺序的版本。

map(posnumber, assoc⁠?: number = 1) → number

通过此映射映射一个位置。

mapResult(posnumber, assoc⁠?: number = 1) → MapResult

通过此映射映射一个位置,返回一个映射结果。

Document transforms

由于你可能经常需要通过将一系列的 steps 合并到一起来修改文档,ProseMirror 提供了一个抽象来使这个过程简单化。 State transactions 就是这个抽象,它是 transforms 的子类。

transaction 通常被简写为 tr。

class Transform

为了构建和跟踪文档 transformation 的一系列 steps 的抽象。

大多数的 transforming 方法返回 Transform 对象本身,因此它们可以链式调用。

new Transform(docNode)

新建一个起始于给定文档的 transform。

steps: Step[]

transform 中的 steps 们。

docs: Node[]

在每个 steps 开始之前的文档们。

mapping: Mapping

一个 maps 了 transform 中的每一个 steps 的 mapping。

doc: Node

当前文档(即应用了 transform 中 steps 后的结果)。

before: Node

起始文档。

step(stepStep) → Transform

对当前 transform 应用一个新的 step,然后保存结果。如果应用失败则抛出一个错误。

错误的类叫做「TransformError」。
maybeStep(stepStep) → StepResult

尝试在当前 transformation 中应用一个 step,如果失败则忽略,否则返回 step result。

docChanged: boolean

如果文档被改变过(当有任何 step 的时候),则返回 true。

replace(
to⁠?: number = from,
slice⁠?: Slice = Slice.empty
) → Transform

用给定的 slice 替换在 fromto 之间的这部分文档。

replaceWith(
contentFragment | Node | readonly Node[]
) → Transform

用给定的内容替换给定过的 range,该内容可能是一个 fragment、节点、或者节点数组。

delete(fromnumber, tonumber) → Transform

删除给定位置之间的内容。

insert(
contentFragment | Node | readonly Node[]
) → Transform

在给定的位置插入给定的内容。

replaceRange(fromnumber, tonumber, sliceSlice) → Transform

fromto和 slice 的openStart属性作为提示来替换文档的一个 range,而不是固定的起点和终点。此方法可能会通过删除被标记为非定义上下文的替换区域的完全覆盖的父节点,或包含切片中标记为定义其内容的开放父节点,以更符合所见即所得的期望,从而扩展替换区域或关闭切片中的开放节点。

这是处理粘贴的方法。例如,类似的replace方法是一个更原始的工具,它不会移动其给定范围的开始和结束,在需要更精确控制的情况下非常有用。

replaceRangeWith(fromnumber, tonumber, nodeNode) → Transform

用给定的 node 替换一个由给定 fromto 模糊确定的 range,而不是一个精确的位置。 如果当 from 和 to 相同且都位于父级节点的起始或者结尾位置,而给定的 node 并不适合此位置的时候,该方法可能将 from 和 to 的范围 扩大 到 超出父级节点以允许给定的 node 被放置,如果给定的 range(from 和 to 形成的)完全覆盖了一个父级节点,则该方法可能完全替换掉这个父级节点。

deleteRange(fromnumber, tonumber) → Transform

删除给定的 range,会将该 range 扩大到完全覆盖父级节点,直到找到一个有效的替换为止。

有些 range 两侧在不同深度的节点中,因此会先将二者的值扩展到与二者中深度与较小的那个保持一致以形成完全覆盖一个父级节点的 range。
lift(rangeNodeRange, targetnumber) → Transform

如果 range 内容前后有同级内容,则会将给定 range 从其父级节点中分割,然后将其沿树移动到由 target 指定的深度。你可能想要使用 liftTarget 来计算 target,以保证这个沿着树的提升是有效的。

join(posnumber, depth⁠?: number = 1) → Transform

将给定位置周围的块级元素连接起来。如果深度是 2,它们最后和第一个同级节点也会被连接,以此类推。

wrap(
wrappersreadonly {typeNodeType, attrs⁠?: Attrs}[]
) → Transform

用规定的包裹节点集合来包裹给定的 range。包裹节点们会被假定是适合当前位置的,其应该被 findWrapping 方法合适的计算出来。

setBlockType(
to⁠?: number = from,
attrs⁠?: Attrs | fn(oldNodeNode) → Attrs | null = null
) → Transform

将介于 fromto 之间的所有的文本块(部分地)设置成带有给定 attributes 的给定的节点类型。

为什么是「部分的」是因为有些文本块或许不能被改变类型。
setNodeMarkup(
type⁠?: NodeType,
attrs⁠?: Attrs | null = null,
marks⁠?: readonly Mark[]
) → Transform

在给定的 pos 处改变节点的类型、attributes、或者/和 marks。如果 type 没有给,则保留当前节点的类型。

setNodeAttribute(posnumber, attrstring, valueany) → Transform

将给定节点的单个属性设置为新值。 pos 地址文档内容。使用 setDocAttribute 设置文档本身的属性。

setDocAttribute(attrstring, valueany) → Transform

将文档上的单个属性设置为新值。

addNodeMark(posnumber, markMark) → Transform

在位置pos的节点添加一个标记。

removeNodeMark(posnumber, markMark | MarkType) → Transform

从位置pos的节点中移除标记(或给定类型的标记)。

split(
depth⁠?: number = 1,
typesAfter⁠?: ({typeNodeType, attrs⁠?: Attrs} | null)[]
) → Transform

在给定位置拆分节点,并且如果 depth 大于 1,还可以选择性地拆分其上方的任意数量的节点。默认情况下,拆分出的部分将继承原始节点的节点类型。这可以通过传递一个类型和属性数组来更改,以便在拆分后使用。

addMark(fromnumber, tonumber, markMark) → Transform

将给定的 mark 添加到 fromto 之间的内联节点中。

removeMark() → Transform

fromto 之间的内联节点上给定的的 mark 移除。当 mark 是一个单独的 mark 时,则精确移除这个 mark。 如果是一个 mark 类型时,则移除所有的该类型的 mark。如果是 null,移除其内所有类型的 mark。

clearIncompatible() → Transform

从给定的 pos 移除与给定的新的父级节点类型不兼容的所有 marks 和节点们。 接受一个可选的起始 content match 作为第三个参数。

当新建一个 transform 或者决定能否新建一个 transform 的时候,下面几个工具函数非常有用。

replaceStep(
docNode,
to⁠?: number = from,
slice⁠?: Slice = Slice.empty
) → Step | null

将一个 slice 「恰当的」放到文档中给定的位置,会生成一个进行插入操作的 step。如果没有一个有意义的途径来插入该 slice,或者插入的 slice 没有意义(比如一个空的 slice 带着空的 range)则返回 null。

liftTarget(rangeNodeRange) → number | null

尝试寻找一个目标深度以让给定的 range 内的内容可以被提升(深度)。不会考虑有属性 isolating 存在的父级节点。

findWrapping(
attrs⁠?: Attrs | null = null,
innerRange⁠?: NodeRange = range
) → {typeNodeType, attrsAttrs | null}[] |

尝试找到一个有效的方式来用给定的节点类型包裹给定 range 的内容。如果必要的话,可能会在包裹节点的内部和周围生成额外的节点。 如果没有可用的包裹方式则返回 null。当 innerRange 给定的时候,该 range 的内容将会被作为被包裹的内容,而不是 range 的内容。

需要研究一下 range 和 innerRange 的区别,若 range 包含多个同级节点和 range 只含有一个节点是否有区别?
canSplit(
docNode,
depth⁠?: number = 1,
typesAfter⁠?: ({typeNodeType, attrs⁠?: Attrs} | null)[]
) → boolean

检查给定的位置是否允许分割。

canJoin(docNode, posnumber) → boolean

测试在给定位置之前或者之后的块级节点是否可以被连接起来。

joinPoint(docNode, posnumber, dir⁠?: number = -1) → number | undefined

寻找一个给定位置的祖先节点,该节点可以被连接到块级节点之前(或者之后,如果 dir 是正的话)。 如果找到,返回这个可加入的位置。

insertPoint(docNode, posnumber, nodeTypeNodeType) → number | null

pos 本身不是有效的位置但位于节点的起始或结尾处时,通过搜索节点的层级结构,尝试找到一个可以在给定位置的节点附近插入给定节点类型的点。 如果找不到位置,则返回 null。

dropPoint(docNode, posnumber, sliceSlice) → number | null

寻找一个位置在给定的位置或者附近,以让给定的 slice能够插入。即使原始的位置并不直接在父级节点的起始或者结束位置, 也会尝试查看父级节点最近的边界然后尝试插入。如果找不到位置,则返回 null。

prosemirror-commands module

本模块导出了一些 命令,它可以构建块级函数以封装一个编辑行为。一个命令函数接受一个编辑器的 state, 一个 可选的 dispatch 函数,用来 dispatch 一个 transaction,及一个 可选的 EditorView 实例作为参数。它应该返回一个布尔值以指示它是否可以执行任何行为。当没有 dispatch 回调被传入的时候, 该命令应该做一个 空运行,以决定它是否应该被运行,而不实际做任何事情。

这些命令大多数用来绑定按键以及定义菜单项。

chainCommands(...commandsreadonly Command[]) → Command

组合多个命令函数到一个单独的函数(一个一个的调用这些命令,直到其中一个返回 true)。

deleteSelection: Command

删除选区,如果存在的话。

joinBackward: Command

如果选区是空(光标)而且在一个文本块的起始位置,则试着减少光标所在的块级节点和该块级节点之前的节点之间的距离。如果存在一个块级节点直接位于 光标所在的块级节点之前而且能够被连接的话,则连接他们。如果不存在,则尝试通过将光标所在的块级节点从其父级节点中提升一级或者将其移动到父级节点之前的 块级节点中的方式来尝试将选择的块级节点(即光标所在的块级及诶单)移动的更接近文档中的下一个节点。如果给定 view 参数,则将会使用之以获取准确的(用来处理 bidi 的)文本块起始方向。

selectNodeBackward: Command

当选区是空且在一个文本块的起始位置的时候,如果可以的话,选中位于文本块之前的节点。这个行为通常倾向于在 joinBackward 之后绑定向后删除键或者其他删除命令,以作为一个回退方案,如果 schema 不允许在选择的点进行删除操作的话。

向后删除键在 Mac 上是 Ctrl + D,会删除光标右侧的内容。
joinTextblockBackward: Command

一种更有限的joinBackward形式,仅在光标位于文本块开头时,尝试将当前文本块与之前的文本块连接起来。

joinForward: Command

如果选区是空而且光标在一个文本块的结尾处,则尝试减少或者移除当前块级元素和它之后的块级元素之间边界。可以通过连接他们或者在树中移动挨着当前块级元素的 其他块级元素。将会使用给定的 view(如果有)以获取准确的文本块起始方向。

selectNodeForward: Command

当选区是空且在一个文本块的结尾位置的时候,如果可以的话,选中位于文本块之后的节点。这个行为通常倾向于在 joinForward 之后绑定删除键或者其他类似的删除命令,以作为一个回退方案,如果 schema 不允许在选择的点进行删除操作的话。

joinTextblockForward: Command

一种更有限的joinForward形式,仅在光标位于文本块末尾时尝试将当前文本块与后面的文本块连接起来。

joinUp: Command

连接选择的块级节点,或者如果有文本选区,则连接与选区最接近的可连接的祖先节点和它之前的同级节点。

joinDown: Command

连接选择的块级节点,或者连接与选区最接近的可连接的祖先节点和它之后的同级节点。

lift: Command

从父级节点中提升选择的块级节点,或者提升与选区最接近的可提升的祖先节点。

newlineInCode: Command

如果选区在一个配置对象中 code 属性为真值的节点类型中,则用一个换行符替换选区。

exitCode: Command

如果选区在一个配置对象中 code 属性为真值的节点类型中,则在当前代码块之后新建一个默认的块级节点, 然后将光标移动到其内。

createParagraphNear: Command

如果一个块级节点被选中,则在其前面(如果它是其父级节点的第一个子元素)新建一个段落,或者其后面。

liftEmptyBlock: Command

如果光标在一个空的可以被提升的文本块中,那么提升这个文本块。

splitBlock: Command

分割选区的父级节点。如果选区是一个文本选区,还会同时删除选区内容。

splitBlockAs(
splitNode⁠?: fn() → {typeNodeType, attrs⁠?: Attrs} | null
) → Command

创建一个splitBlock的变体,使用自定义函数来确定新分离块的类型。

splitBlockKeepMarks: Command

行为和 splitBlock 类似,不过不会重置光标处已经激活的 marks 集合。

selectParentNode: Command

移动选区到包裹当前选区的节点中(如果有的话,不会选择文档根节点)。

selectAll: Command

选择整个文档。

selectTextblockStart: Command

将光标移动到当前文本块的开头。

selectTextblockEnd: Command

将光标移动到当前文本块的末尾。

wrapIn(
attrs⁠?: Attrs | null = null
) → Command

用带有给定 attributes 的给定类型的节点来包裹选区。

setBlockType(
attrs⁠?: Attrs | null = null
) → Command

返回一个尝试将选中的文本块设置为带有给定 attributes 的给定节点类型的命令。

toggleMark(
attrs⁠?: Attrs | null = null,
) → Command

新建一个命令函数,控制带有给定 attributes 的给定 mark 的开关。如果当前选区不支持这个的 mark 将会返回 false。 这个过程将会移除在选区中存在的任何该种类型的 mark,或者如果没有的话则添加之。如果选区是空,则这将会应用到 stored marks](#state.EditorState.storedMarks) 中,而不是文档的某个范围。

options
removeWhenPresent⁠?: boolean

控制当所选范围的一部分已经有标记而另一部分没有时,标记是被移除(true,默认)还是被添加(false)。

enterInlineAtoms⁠?: boolean

当设置为 false 时,这将阻止命令对标记为原子的内联节点的内容进行操作,这些节点完全被选区范围覆盖。

autoJoin(
isJoinablefn(beforeNode, afterNode) → boolean |
readonly string[]
) → Command

封装一个命令,以便当它产生一个 transform 以引起两个可连接的节点彼此相邻时能够被连接。 当节点具有相同类型并且当 isJoinable 参数是函数时返回 true 或者参数是一个字符串数组而这些节点名在这个数组中时,节点将会被认为是可连接的。

baseKeymap: Object<Command>

取决于删除操作的平台,该值将指向 pcBasekeymap 或者 macBaseKeymap

pcBaseKeymap: Object<Command>

一个基本的按键映射,包含不特定于任何 schema 的按键绑定。绑定包含下列按键(当多个命令被列出来的时候,它们被 chainCommands 所链接起来)。

  • 输入newlineInCodecreateParagraphNearliftEmptyBlocksplitBlock
  • Mod-EnterexitCode
  • 退格键Mod-退格键deleteSelectionjoinBackwardselectNodeBackward
  • 删除Mod-删除deleteSelectionjoinForwardselectNodeForward
  • Mod-删除deleteSelectionjoinForwardselectNodeForward
  • Mod-aselectAll
macBaseKeymap: Object<Command>

pcBaseKeymap 的复制版,和 Backspace 一样绑定了 Ctrl-h,和 Delete 一样绑定了 Ctrl-d, 和 Ctrl-Backspace 一样绑定了 Alt-Backspace,和 Ctrl-Delete 一样绑定了 Ctrl-Alt-Backspace, Alt-Delete, 和 Alt-d

prosemirror-history module

一个 ProseMirror 的撤销/重做历史的实现。撤销/重做的历史是 可选择的,这意味着它不仅仅是回滚到 之前的 state,而是可以在撤销部分修改的同时保留其他的修改,或者原封不动的保留之后的修改(这对于协同编辑来说 是很有必要的,在其他一些情况下也可能会发生)。

history(config⁠?: Object = {}) → Plugin

返回一个插件以使编辑器撤销历史可用。该插件将会追踪撤销和重做的操作栈,这可以和 撤销 and 重做 命令一同使用。

你可以在一个 transaction 上设置一个 "addToHistory"metadata 属性false,来阻止该 tr 被撤销回滚。

设置了这个之后,该 tr 就相当于不会被加入到历史栈中。
config
depth⁠?: number

在最早的操作历史被丢弃之前,历史栈的最大长度,默认是 100。

超过了就先丢弃最早的操作记录。
newGroupDelay⁠?: number

操作从开始算起应该持续多久才会被算作一个操作。默认是 500(毫秒)。记住,如果多个修改不相邻的话,总是会被算作是新的操作。

如果该值被设置为 500,则在 500 毫秒内输入 10 个字,这样会触发 10 个 tr,但是会被归为一「组」,撤销的时候直接撤销这 10 个字的输入记录。 如果 500 毫秒内输入 20 个字同理撤销这 20 个字的记录。如果在 500 毫秒内输入了 10 个字,在 501 毫秒内输入了第 11 个字,则撤销的时候,那第 501 毫秒输入的第 11 个字被算做是一组,先撤销那 1 个字的输入,再撤销那 10 个字的输入。这个逻辑是为了和系统输入保持一致,各位可以试试在系统的某个界面如搜索框变换输入速度来输入内容后 撤销,看会发生什么。
undo: Command

一个可以撤销最后修改(如果有)的命令函数。

redo: Command

一个可以重做最后一次撤销修改(如果有)的命令函数。

undoNoScroll: Command

一个撤销上次更改的命令功能。不要滚动选择到视图中。

redoNoScroll: Command

一个重做上次撤销更改的命令功能。不要滚动选择到视图中。

undoDepth(stateEditorState) → any

在给定的 state 中可以被撤销的操作的数量。

redoDepth(stateEditorState) → any

在给定的编辑器 state 中可以被重做的操作的数量。

closeHistory(trTransaction) → Transaction

在给定的 transaction 上设置一个标记,这将会阻止接下来的 steps 们被附加到一个已经存在的历史事件中(这样就需要一个单独的撤销命令来撤销了)。

这个函数不能顾名思义的以为不会会将 tr 加入到历史栈中。正确理解应该是其会将本来能够一个 tr n 个 steps 完成的修改, 变成了 两个 tr,n/2 个 step。因此需要额外的撤销命令来撤销修改。「close」表示紧急中断该 tr,然后将剩余的 steps 放到一个新的 tr 中。

prosemirror-collab module

这个模块实现了一个 API,该 API 可以供协同编辑使用。查看 指南 以获取更多细节和示例。

collab(config⁠?: Object = {}) → Plugin

创建一个能使编辑器支持协同编辑框架的插件。

config
version⁠?: number

协同编辑的起始版本号,默认是 0.

clientID⁠?: number | string

客户端 ID,用来分别哪些修改是自己做的哪些是其他客户端做的。默认是一个随机的 32 位数字。

getVersion(stateEditorState) → number

获取 collab 插件与鉴权中心同步的版本。

receiveTransaction(
stepsreadonly Step[],
clientIDsreadonly (string | number)[],
options⁠?: Object = {}
) → Transaction

创建一个接受自鉴权中心的表示新 steps 集合的 transaction。应用该 transaction 以将 state 向前移动来适应文档的鉴权中心的视图。

「鉴权中心」指的就是协同处理的服务端,那里负责处理接受那些 tr,拒绝哪些 tr。
options
mapSelectionBackward⁠?: boolean

启用后(默认是 false),如果当前选区是一个 文本选区,则它的两侧位置会被这个 transaction 通过一个负向偏移 mapped,以便使插入光标处的内容会以光标所在的位置结尾。用户通常倾向于这样做,不过因为向后兼容的 原因,默认情况下不会这么做。

sendableSteps(stateEditorState) → {
versionnumber,
stepsreadonly Step[],
clientIDnumber | string,
originsreadonly Transaction[]
} |

提供编辑器未被确认的 steps 的数据描述,它会被发送给鉴权中心。如果没有需要发送的东西,返回 null。

origins 值是产生每个 steps 的 原始 transactions。对于寻找 steps 的时间戳和其他 metadata 信息很有用,不过记住,steps 可能会被 rebased, 因此原始的 transaction 仍然是旧的,未改变的对象。

prosemirror-keymap module

一个为了方便的定义按键绑定的插件。

keymap(bindingsObject<Command>) → Plugin

用给定的绑定集合来创建一个按键映射插件。

绑定应该将按键名和 命令 格式的函数对应起来,该函数将会传入 (EditorState, dispatch, EditorView) 作为参数来调用,如果它响应了该按键按下,则应该返回 true。记住,view 参数并不是命令协议的一部分,但是如果按键绑定需要直接与 UI 交互,则可以将其用来作为应急出口使用。

按键的名字可以是形如 "Shift-Ctrl-Enter" 的字符串,它是一个按键标识符,可以有 0 个或者多个修饰符做前缀。按键标识符的基础字符串基于这个 KeyEvent.key 中的按键而来。使用小写字母来表示字母键 (或者使用大写字母,如果你想要处理 shift 被同时按下的情况)。你也许会使用 "Space" 作为 " " 的别名。

修饰符可以以任何顺序给定。只允许 Shift-(或者 s-),Alt-(或者 a-),Ctrl-(或 c-Control-)及 Cmd-(或 m-Meta-) 这些修饰符出现。对于通过按下 shift 创建的字符,则 Shift- 前缀就是隐式的,不应该再显式添加了。

在 Mac 上你可以使用 Mod- 作为 Cmd- 的简称,在其他平台可以使用 Ctrl-

你可以在编辑器中添加多个按键映射插件。它们出现的顺序决定了它们的优先级(数组前面的优先被 dispatch)。

keydownHandler(bindingsObject<Command>) → fn(viewEditorView, eventKeyboardEvent) → boolean

给定一组绑定(使用与keymap相同的格式),返回一个处理它们的keydown处理程序

prosemirror-inputrules module

本模块定义了一个编辑器插件用来附加 input rules(输入规则),它可以响应或者转换用户输入的文字。 本模块还带有一些默认的规则,可以通过本插件启用。

class InputRule

输入规则是一些正则表达式,描述了输入何种文本会引起一些额外的变化。这个变化可能是将两个短斜杠变成一个长破折号,或者将以 "> " 开头的段落用 blockquote 包裹着, 亦或者其他完全不一样的事情。

new InputRule(
options⁠?: Object = {}
)

创建一个输入规则。规则会应用到当用户输入一些内容的时候,且直接在光标之前的文本会匹配 match 参数,该参数应该合适的用 $ 结尾。

handler 参数可以是一个字符串,这种情况下表示匹配的文本,或者在正则中匹配的第一个组,会被该字符串替换。

它也可以是一个函数,会将调用 RegExp.exec 后产生的 结果匹配数组传入作为参数来调用,以及匹配的起始和结束的范围。函数返回一个描述了规则影响的 transaction,或者如果输入没有被处理则返回 null。

options
undoable⁠?: boolean

当设置为 false 时,undoInputRule 不适用于此规则。

inCode⁠?: boolean | "only"

默认情况下,输入规则不会应用于标记为code的节点。将其设置为true以更改此设置,或设置为"only"在此类节点中匹配。

inCode: boolean | "only"
inputRules({rulesreadonly InputRule[]}) → Plugin<
{transformTransaction, fromnumber, tonumber, textstring} |
>

创建一个输入规则插件。启用的话,将会导致与任何给定规则匹配的文本输入都会触发该规则对应的行为。

undoInputRule: Command

如果应用这个规则是用户做的最后一件事情的话,这是一个可以撤销输入规则的命令。

本模块还带有一些预定义的规则:

emDash: InputRule

转换两个短斜杠为一个长破折号的输入规则。

ellipsis: InputRule

转换三个点为一个省略号的输入规则。

openDoubleQuote: InputRule

「智能」打开双引号的输入规则。

closeDoubleQuote: InputRule

「智能」关闭双引号的输入规则。

openSingleQuote: InputRule

「智能」打开单引号的输入规则。

closeSingleQuote: InputRule

「智能」关闭单引号的输入规则。

smartQuotes: readonly InputRule[]

自动打开/关闭 单/双 引号相关的输入规则。

下列这些工具函数接受一个特定于 schema 的参数,并创建一个特定于 schema 的输入规则。

wrappingInputRule(
getAttrs⁠?: Attrs | = null,
) → InputRule

当给定字符串被输入的时候构建一个输入规则以自动包裹一个文本块。regexp 参数被直接传给 InputRule 构造函数。你也许想要正则表达式以 ^ 开头, 这样的话就只会从一个文本块起始位置开始匹配。

^ 表示正则中的起始位置匹配,一般用来做类似于 markdown 的输入规则,例如在文本块开头输入 # + 空格后,生成一个 h 元素。

nodeType 是要被包裹进的节点类型。如果它需要 attributes,那么你既可以直接传入,也可以传入一个计算 attributes 的函数,该函数接受正则匹配的结果作为参数。

默认情况下,如果有新的包裹节点之前有一个与之相同类型的节点,那么这个规则将会尝试 join(连接) 这两个节点。 你可以传递一个连接指示函数,它接受一个正则表达式的结果和在包裹节点之前的节点作为参数,返回一个指示连接是否应该进行的布尔值。

textblockTypeInputRule(
getAttrs⁠?: Attrs | = null
) → InputRule

构建一个输入规则,以当匹配的文本输入的时候能够改变文本块的类型。你的正则通常应该以 ^ 开头,这样它就会只匹配文本块的起始位置。 可选参数 getAttrs 可以被用来计算新节点的 attributes,功能和 wrappingInputRule 中的该函数一样。

prosemirror-gapcursor module

本插件为那些不允许正常选区的聚焦的位置(比如说该位置有叶子节点、表格、或者文档的起始和结尾处)添加一种选区的类型。

你可能需要加载 style/gapcursor.css 这个文件,它包含了模拟光标的基本样式(即短的,闪烁的水平条纹)。

默认情况下,gap 光标只允许放置于默认内容节点(通过 schema 的 content 限制)是文本节点的地方。你可以通过在节点配置对象中添加 allowGapCursor 属性来自定义这个行为,如果该值是 true,则 gap 光标被允许放置到该节点的任何位置,如果是 false 则表示永远不允许放置该种类型的光标。

gapCursor() → Plugin

创建一个 gap 光标插件。如果启用的话,它将会捕获点击区域附近的和方向键经过的不允许有一个正常的可选择区域的地方,然后为它们创建一个 gap 光标选区。 光标元素的类名是 ProseMirror-gapcursor。你既可以从该包中直接引入 style/gapcursor.css 做样式文件,也可以添加你自己的样式以使它可见。

class GapCursor extends Selection

这个类是 Gap 光标选区的表现形式。它的 $anchor$head 属性都指向光标的位置。

new GapCursor($posResolvedPos)

创建一个间隙光标。

prosemirror-schema-basic module

本模块定义了一个简单的 schema。你可以直接拿来使用,或者扩展它,亦或者仅仅是抄其中的一些节点和 mark 的配置对象然后应用到新的 schema 中。

schema: Schema<
"blockquote" |
"doc" |
"paragraph" |
"horizontal_rule" |
"heading" |
"code_block" |
"text" |
"image" |
"hard_break"
,
"code" | "em" | "strong" | "link"
>

该 schema 大致对应于 CommonMark 使用的文档 schema,减去在 prosemirror-schema-list 模块中定义的里列表元素。

为了能够从该 schema 中重用元素,可以扩展和读取 spec.nodesspec.marks 属性

nodes: Object

定义在该 schema 中节点们的 Specs(配置对象)

doc: NodeSpec

NodeSpec 顶级文档节点。

paragraph: NodeSpec

普通段落文本块。在 DOM 中表现为一个 <p> 元素。

blockquote: NodeSpec

一个引用块(<blockquote>)包裹一个或者多个块级节点。

horizontal_rule: NodeSpec

水平分隔线(<hr>)。

heading: NodeSpec

标题文本块,带有一个 level 属性,该属性的值应该在 1 到 6 的范围。会被格式化和序列化为 <h1><h6> 元素。

code_block: NodeSpec

代码块。默认情况下不允许 marks 和非文本行内节点。表现为一个包裹着 <code> 元素的 <pre> 元素。

text: NodeSpec

文本节点。

image: NodeSpec

行内图片节点。支持 srcalthref 属性。后两者默认的值是空字符串。

hard_break: NodeSpec

强制换行符,在 DOM 中表示为 <br> 元素。

marks: Object

schema 中 marks 们的 Specs(配置对象)

链接。有 hreftitle 属性。title 默认是空字符串。会被渲染和格式化为一个 <a> 元素。

em: MarkSpec

强调。渲染为一个 <em> 元素,格式化规则同样匹配 <i>font-style: italic

这里可能是 em 加粗,或者 i/font-style: italic 斜体。
strong: MarkSpec

加粗。渲染为 <strong>,格式化规则同样匹配 <b>font-weight: bold

code: MarkSpec

行内代码。表现为 <code> 元素。

prosemirror-schema-list module

orderedList: NodeSpec

一个有序列表的 节点配置对象。有一个唯一的属性 order,它决定了列表从哪个数字开始计数,默认是 1。 表现形式是一个 <ol> 元素。

bulletList: NodeSpec

一个无序列表的节点配置对象,DOM 表示为 <ul>

listItem: NodeSpec

列表项(<li>)的配置对象。

li 父节点可以是 ol 也可以是 ul,高级用法还可以扩展为 todo。
addListNodes() → OrderedMap<NodeSpec>

为 schema 方便的添加列表相关的节点类型到一个特定的节点类型的函数。orderedList 表示为 "ordered_list"bulletList 表示为 "bullet_list", 以及 listItem 表示为 "list_item"

itemContent 决定了列表项的内容表达式。如果你想要在本模块中定义的命令应用于你自己的列表结构,则它的值应该是诸如 "paragraph block*" 或者 "paragraph (ordered_list | bullet_list)*" 之类的。listGroup 可以将列表节点类型分配到一个组名,比如 "block"

wrapInList(
attrs⁠?: Attrs | null = null
) → Command

返回一个命令函数,该函数用给定的类型和属性包裹位于 list 中的选区。如果 dispatch 参数是 null,则只返回一个指示该行为是否可能的值, 而不实际执行修改。

wrapRangeInList(
attrs⁠?: Attrs | null = null
) → boolean

尝试将给定的节点范围包装在给定类型的列表中。 返回 true 如果这是可能的,false 否则。当 tr 非空时,将包装添加到该事务中。当它是 null 时,该函数仅查询是否可以进行包装。

splitListItem(itemTypeNodeType, itemAttrs⁠?: Attrs) → Command

构建一个命令,它会通过分割列表项的直接子元素的非空文本节点的方式来分割一个列表项。

splitListItemKeepMarks(itemTypeNodeType, itemAttrs⁠?: Attrs) → Command

splitListItem,但不重置光标处的活动标记集。

liftListItem(itemTypeNodeType) → Command

创建一个命令,该命令会提升选区所在的列表项到上一级列表中。

sinkListItem(itemTypeNodeType) → Command

创建一个命令,该命令会将选区所在的列表项缩进到一个内部列表中去。