操作块
下面,我们介绍 editor
上的方法,你可以使用它们从编辑器读取块,以及如何创建 / 删除 / 更新块:
document
getBlock
getPrevBlock
getNextBlock
getParentBlock
forEachBlock
insertBlocks
updateBlock
removeBlocks
replaceBlocks
moveBlocksUp
moveBlocksDown
canNestBlock
nestBlock
canUnnestBlock
unnestBlock
常用类型
在深入方法之前,先讨论一些参数中常用的类型:
块标识符
访问、插入、更新、删除或替换块的方法,可能需要一个 BlockIdentifier
作为文档中已有块的引用。
这可以是表示块 ID 的字符串,或者是包含该 ID 的 Block
对象:
type BlockIdentifier = string | Block;
部分块(Partial Blocks)
当从编辑器获取块时,始终返回完整的 Block
对象。
对于更新或创建块,你不需要传递所有属性,可以使用 PartialBlock
类型:
type PartialBlock = {
id?: string;
type?: string;
props?: Partial<Record<string, any>>; // 精确类型依赖于 "type"
content?: string | InlineContent[] | TableContent;
children?: PartialBlock[];
};
PartialBlock
对象与普通 Block
对象非常相似,但所有成员都是可选的,且 props
是部分的。这样可以简化更新或创建简单块的操作。我们后面会看到示例。
访问块
有几种不同的方法可以从编辑器中检索块:
获取文档
使用以下调用获取编辑器中文档的快照(所有顶层、非嵌套块):
document: Block[];
// 用法
const blocks = editor.document;
returns:
文档;编辑器中所有顶层(非嵌套)块的快照。
我们曾在 Document JSON 示例中使用过它。
获取特定块
单个特定块
使用 getBlock
获取编辑器中特定块的快照:
getBlock(blockIdentifier: BlockIdentifier): Block | undefined;
// 用法
const block = editor.getBlock(blockIdentifier);
blockIdentifier:
要检索的现有块的标识符。
returns:
具有相应标识符的块,如果未找到匹配块则返回 undefined
。
上一个块
使用 getPrevBlock
获取指定块的前一个同级块的快照:
getPrevBlock(blockIdentifier: BlockIdentifier): Block | undefined;
// 用法
const prevBlock = editor.getPrevBlock(blockIdentifier);
blockIdentifier:
要获取前一个同级块的现有块的标识符。
returns:
该块的前一个同级块,若未找到匹配块,或者该块是文档中的第一个块或父块的第一个子块,则返回 undefined
。
下一个块
使用 getNextBlock
获取指定块的下一个同级块的快照:
getNextBlock(blockIdentifier: BlockIdentifier): Block | undefined;
// 用法
const nextBlock = editor.getNextBlock(blockIdentifier);
blockIdentifier:
要获取下一个同级块的现有块的标识符。
returns:
该块的下一个同级块,若未找到匹配块,或者该块是文档中的最后一个块或父块的最后一个子块,则返回 undefined
。
父块
使用 getParentBlock
获取指定块的父块快照:
getParentBlock(blockIdentifier: BlockIdentifier): Block | undefined;
// 用法
const parentBlock = editor.getParentBlock(blockIdentifier);
blockIdentifier:
要获取父块的现有块的标识符。
returns:
该块的父块,若未找到匹配块,或者该块不嵌套在任何父块中,则返回 undefined
。
遍历所有块
使用 forEachBlock
深度优先遍历编辑器中的所有块,并对每个块执行回调:
forEachBlock(
callback: (block: Block) => boolean | undefined,
reverse: boolean = false
): void;
// 用法
editor.forEachBlock((block) => {...});
callback:
对每个块执行的回调函数。返回 false
可以停止遍历。
reverse:
是否反向遍历块。
获取鼠标悬停 / 选中的块
查看 光标与选择 以了解如何获取用户正在交互的块。
插入新块
使用 insertBlocks
将新块插入文档:
insertBlocks(
blocksToInsert: PartialBlock[],
referenceBlock: BlockIdentifier,
placement: "before" | "after" = "before"
): void;
// 用法
editor.insertBlocks([{type: "paragraph", content: "Hello World"}], referenceBlock, placement)
blocksToInsert:
要插入的一组部分块。
referenceBlock:
用作插入位置参考的现有块的标识符。
placement:
新块是插入在 referenceBlock
之前还是之后。
如果块的 id
未定义,BlockNote 会自动生成一个。
如果找不到参考块,该方法会抛出错误。
更新块
使用 updateBlock
更新现有块:
updateBlock(
blockToUpdate: BlockIdentifier,
update: PartialBlock
): void;
// 示例:将块类型改为段落
editor.updateBlock(blockToUpdate, { type: "paragraph" });
blockToUpdate:
要更新的现有块的标识符。
update:
一个定义了如何更改现有块的部分块。
因为 blockToUpdate
是 PartialBlock
对象,某些字段可能未定义,这些未定义字段会保持现有块的值。
如果未找到要更新的块,会抛出错误。
删除块
使用 removeBlocks
删除文档中的现有块:
removeBlocks(
blocksToRemove: BlockIdentifier[],
): void;
// 用法
editor.removeBlocks(blocksToRemove)
blocksToRemove:
要删除的现有块的标识符数组。
如果找不到任一需要删除的块,方法会抛出错误。
替换块
使用 replaceBlocks
用新块替换编辑器中的现有块:
replaceBlocks(
blocksToRemove: BlockIdentifier[],
blocksToInsert: PartialBlock[],
): void;
// 用法
editor.replaceBlocks(blocksToRemove, blocksToInsert)
blocksToRemove:
要替换的现有块的标识符数组。
blocksToInsert:
替换用的新块数组,元素是部分块。
若要删除的块不相邻或处于不同嵌套层级,blocksToInsert
会插入到 blocksToRemove
中第一个块的位置。
如果找不到任一要删除的块,会抛出错误。
向上 / 向下移动块
向上移动
使用 moveBlocksUp
向上移动选中的块:
moveBlocksUp(): void;
// 用法
editor.moveBlocksUp();
向下移动
使用 moveBlocksDown
向下移动选中的块:
moveBlocksDown(): void;
// 用法
editor.moveBlocksDown();
嵌套与取消嵌套块
BlockNote 还提供了函数,可以对包含文本光标的块进行嵌套和取消嵌套。
嵌套块
使用 canNestBlock
检查包含文本光标的块是否可以嵌套 (即其上方在相同嵌套层级存在一个块):
canNestBlock(): boolean;
// 用法
const canNestBlock = editor.canNestBlock();
然后,使用 nestBlock
实际对该块进行嵌套(缩进):
nestBlock(): void;
// 用法
editor.nestBlock();
取消嵌套块
使用 canUnnestBlock
检查包含文本光标的块是否可以取消嵌套(即是否嵌套在另一个块中):
canUnnestBlock(): boolean;
// 用法
const canUnnestBlock = editor.canUnnestBlock();
然后,使用 unnestBlock
对该块取消嵌套:
unnestBlock(): void;
// 用法
editor.unnestBlock();
低阶 API
BlockNote 事务
transact
方法用于对编辑器执行一系列更改。它可以将多个更改合并为单个撤销/重做操作。它也提供了读取状态和执行编辑器更改的低级 API。
transact<T>(callback: (tr: Transaction) => T): T;
callback:
一个接收 Transaction
对象并返回值的函数。
多个编辑器操作
transact
方法可以将多个编辑器操作分组为单个撤销/重做操作。
editor.transact(() => {
// 这两个更改被合并为一个撤销/重做操作
editor.insertBlocks([{ type: "paragraph", content: "Hello World" }], "root");
editor.updateBlock("root", { type: "heading" });
});
读取状态
transact
方法也可以用来读取编辑器状态。
const isSelectionEmpty = editor.transact((tr) => {
// 这里返回的值将作为 transact 的返回值
return tr.selection.empty
});
执行更改
transact
方法还可以用来对编辑器执行更改。
editor.transact((tr) => {
// 当 transact 方法返回时,这个 tr 会自动应用到编辑器
tr.insertText("Hello World");
});
执行 ProseMirror 命令
exec
方法可用于执行一个 ProseMirror 命令。这主要是为了与 ProseMirror 命令的向后兼容。
尽可能应优先使用 transact
方法,因为它会自动处理事务的分发,并能配合 BlockNote 事务工作。此外 exec
方法不能在 transact
调用内部使用,因为两者会相互冲突。
exec(command: (state: EditorState, dispatch?: (tr: Transaction) => void, view?: EditorView) => boolean): boolean;
command:
一个接收当前编辑器状态和可选分发函数的命令函数。
返回 true
表示命令被执行,false
表示未执行。
editor.exec((state, dispatch, view) => {
const tr = state.tr;
if (dispatch) {
tr.insertText("Hello World");
dispatch(tr);
}
return true;
});
这将在光标当前位置插入文本 "Hello World"。
检查 ProseMirror 命令是否可执行
canExec
方法用来检查 ProseMirror 命令是否可以执行。
同样,尽可能应优先使用 transact
方法,因为它自动处理事务分发,且支持 BlockNote 事务。exec
不能在 transact
内部使用,避免冲突。
canExec(command: (state: EditorState, dispatch?: (tr: Transaction) => void, view?: EditorView) => boolean): boolean;
command:
接收当前编辑器状态和可选分发函数的命令函数。
返回 true
表示命令可以执行,false
表示不能执行。
const canReplaceSelection = editor.canExec((state, dispatch, view) => {
if (state.selection.from === state.selection.to){
return false;
}
if (dispatch) {
dispatch(state.tr.insertText("Hello World"));
}
return true;
});
仅当光标不为空时,这将在编辑器中插入文本 "Hello World"。