自定义区块类型
除了 BlockNote 提供的默认区块类型外,您还可以使用 React 组件自行创建自定义区块。请看下面的演示示例,我们在 BlockNote 编辑器中添加了一个自定义警告区块,同时添加了一个自定义的 斜杠菜单项 用于插入该区块。
创建自定义区块类型
使用 createReactBlockSpec 函数来创建自定义区块类型。该函数接受三个参数:
function createReactBlockSpec(
blockConfig: CustomBlockConfig,
blockImplementation: ReactCustomBlockImplementation,
extensions?: BlockNoteExtension[],
): (options? BlockOptions) => BlockSpec;它返回一个函数,您可以调用该函数来创建自定义区块的实例,或者创建一个 BlockSpec。该 BlockSpec 随后会传入您的 BlockNote schema,以将区块添加到编辑器中。此函数也可以接受任意选项,您可以在下方了解更多相关内容。
让我们来看演示中的自定义警告区块,并逐一说明各字段的作用:
const createAlert = createReactBlockSpec(
{
type: "alert",
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
type: {
default: "warning",
values: ["warning", "error", "info", "success"],
},
},
content: "inline",
},
{
render: (props) => {
...
},
}
);区块配置 (CustomBlockConfig)
区块配置描述了自定义区块的结构。用它来指定区块的类型、属性(props)以及内容支持:
type BlockConfig = {
type: string;
content: "inline" | "none";
readonly propSchema: PropSchema;
};type: 定义自定义区块的标识符。
content: 如果您的自定义区块应支持富文本内容,则设置为 inline,否则设置为 none。
在警告示例中,我们希望用户可以在警告区块中输入文本,因此将 content 设为 "inline"。
propSchema: PropSchema 指定区块支持的属性。区块属性(props)是存储在文档中的数据,可用于自定义其外观或行为。
type PropSchema<PrimitiveType extends "boolean" | "number" | "string"> = Record<
string,
| {
default: PrimitiveType;
values?: PrimitiveType[];
}
| {
default: undefined;
type: PrimitiveType;
values?: PrimitiveType[];
}
>;[key: string] 是属性的名称。如果您希望它有默认值,应定义为包含以下属性的对象:
-
default:指定属性的默认值,由此推断出PrimitiveType。 -
values?:指定属性可取的值数组,例如用于限制值为预定义字符串列表。如果未定义values,BlockNote 会假设该属性可以为任意PrimitiveType的值。
如果您不希望该属性有默认值,可以定义为包含以下属性的对象:
-
default:置为undefined,表示无默认值。 -
type:指定该属性允许的PrimitiveType,因为默认值为undefined无法推断类型。 -
values?:指定该属性可取的值数组,用于限制为预定义字符串列表。如果未定义values,BlockNote 会假设该属性可以为任意PrimitiveType的值。
在警告示例中,我们添加了名为 type 的属性,用于指定警告类型(警告 / 错误 / 信息 / 成功),还添加了从默认区块属性继承的文本对齐和文本颜色属性,以支持基本样式选项。
isSelectable?: 可以设置为 false,使区块不可被鼠标和键盘选中。这也有助于能选中区块中不可编辑的内容。仅当 content 为 none 时才应设置为 false,默认为 true。
hardBreakShortcut?: 定义用于在区块的内联内容中插入硬换行的键盘快捷键。默认为 "shift+enter"。
文件区块配置
区块实现 (ReactCustomBlockImplementation)
区块实现定义了区块在编辑器中的渲染方式,以及如何从 HTML 解析和转换为 HTML。
type ReactCustomBlockImplementation = {
render: React.FC<{
block: Block;
editor: BlockNoteEditor;
contentRef?: (node: HTMLElement | null) => void;
}>;
toExternalHTML?: React.FC<{
block: Block;
editor: BlockNoteEditor;
contentRef?: (node: HTMLElement | null) => void;
}>;
parse?: (element: HTMLElement) => PartialBlock["props"] | undefined;
runsBefore?: string[];
meta?: {
hardBreakShortcut?: "shift+enter" | "enter" | "none";
selectable?: boolean;
fileBlockAccept?: string[];
code?: boolean;
defining?: boolean;
isolating?: boolean;
};
};render: 这是您的 React 组件,定义了自定义区块在编辑器中的渲染方式,接收三个 React props:
-
block:要渲染的区块。其类型和属性将与区块配置中定义的类型和 PropSchema 匹配。 -
editor:区块所在的 BlockNote 编辑器实例。 -
contentRef:一个 Reactref,可用来标记区块中哪些元素可编辑,仅当区块配置包含content: "inline"时可用。
toExternalHTML?: 当区块导出到 HTML(例如复制到剪贴板)时使用的组件。如果未定义,BlockNote 会使用 render 进行 HTML 转换。接收与 render 相同的 props。
请注意,传入 toExternalHTML 的组件会在一个独立的 React 根实例中渲染和序列化,因此无法使用依赖于 React 上下文的 Hooks。
parse?: parse 函数定义了如何将 HTML 内容解析为您的区块,例如从剪贴板粘贴内容时调用。如果元素应解析为您的自定义区块,则返回该区块应有的 props;否则返回 undefined。接收一个参数:
element: 正在解析的 HTML 元素。
runsBefore?: 如果该区块的解析或扩展需要优先于其他区块,可在此传入它们的 type 数组。
meta?: 用于设置区块的各种通用属性的对象。
-
hardBreakShortcut?:定义用于在区块的内联内容中插入硬换行的键盘快捷键。默认为"shift+enter"。 -
selectable?:可以设置为 false,使区块不可被鼠标和键盘选中。这也有助于能选中区块中不可编辑的内容。仅当content为none时才应设置为 false,默认为 true。 -
fileBlockAccept?:针对自定义的文件区块,指定上传文件时接受的 MIME 类型。所有文件区块都应指定该属性,并在render函数中使用FileBlockWrapper/ResizableFileBlockWrapper组件(详见下一小节)。 -
code?:该区块是否包含 代码。 -
defining?:该区块是否为 defining。 -
isolating?:该区块是否为 isolating。
区块扩展
尽管本页示例未使用,createReactBlockSpec 接受第三个可选参数 extensions。用于添加特定于该区块的编辑器 extensions,您可以在 此处 了解更多。
区块扩展通常用于添加键盘快捷键,以便将当前区块类型切换为自定义区块。例如,一个目录区块的扩展可能会添加一个 ProseMirror 插件,用以扫描标题并生成目录。
区块配置选项
在某些情况下,您可能希望区块配置可自定义。例如,您可能希望代码区块支持不同的语法高亮(如 web 或嵌入式代码),或标题区块支持灵活的标题级别数。您可以对该用例使用相同的 API,做一些小改动:
// 区块可接受的任意选项,例如标题级别数或可用的代码高亮语言。
type CustomBlockConfigOptions = {
...
}
const createCustomBlock = createReactBlockSpec(
createBlockConfig((options: CustomBlockConfigOptions) => ({
type: "customBlock"
propSchema: ...,
content: ...,
})),
(options: CustomBlockConfigOptions) => ({
render: ...,
...
})
)
const options: CustomBlockConfigOptions = {
...
};
const schema = BlockNoteSchema.create().extend({
blockSpecs: {
// 创建定制区块的实例,并添加进 schema。
customBlock: createCustomBlock(options),
},
});你可以看到,我们不再为配置和实现传入普通对象,而是传入函数。这些函数接收区块选项作为参数,并分别返回配置和实现对象。另外,用于创建配置的函数被 createBlockConfig 包裹。
另外请注意,本页示例中,我们通过调用 createAlert()(无参数)创建了新的 Alert 区块实例。而对自定义区块若有选项,则可按上述方式传入。
要查看使用区块选项的完整示例,请参考 内置标题区块。
将自定义区块添加到编辑器
最后,使用您定义的自定义区块创建一个 BlockNoteSchema:
const schema = BlockNoteSchema.create({
blockSpecs: {
// 如需启用默认区块,请使用展开语法
...defaultBlockSpecs,
// 添加您自己的自定义区块:
alert: createAlert(),
},
});然后您可以按照自定义 Schema页面中说明的,使用此自定义 Schema 实例化您的编辑器。
改善用户体验
现在您已经知道如何创建自定义区块并将其添加到编辑器,但用户尚无方式往文档中创建该区块实例。
为了解决这个问题,建议实现一个命令以在斜杠菜单中插入您的自定义区块,以及在区块类型选择器中为其添加一个条目。