源代码预览块

有些块以源代码形式编写,但以代码生成的内容来展示会更有用——例如将 LaTeX 公式渲染为公式,或将 Mermaid 源代码渲染为图表。与代码块不同,这些块会直接显示渲染后的预览,而源代码则在弹出窗口中编辑。

此页面上的组件仅适用于 React (@blocknote/react)。

BlockNote 的数学图表块就是基于此模式构建的,同时也提供了相同的构建模块来创建你自己的内容:

  • SourceBlockWithPreview(来自 @blocknote/react)——用于自定义块
  • SourceInlineContentWithPreview(来自 @blocknote/react)——用于自定义内联内容

两者都会将你提供的预览渲染到块/内联内容所在的位置,并为你管理可编辑的源代码弹出窗口。弹出窗口的行为本身由编辑器级扩展驱动,BlockNote 默认会注册这些扩展——spec 只需在其 meta 中设置 hasPreview: true 即可选择启用。

自定义块

一个源代码预览块由三个部分组成——下面的示例实现了全部三个部分:

1. 一个包含“plain”内容的块配置——源代码以块的纯文本内容形式存储:

const createMyBlockConfig = createBlockConfig(
  () =>
    ({
      type: "myBlock" as const,
      propSchema: {},
      content: "plain" as const,
    }) as const,
);

2. 一个渲染组件,用于读取源代码,以你喜欢的方式渲染,并将结果传递给 SourceBlockWithPreview

// The block's content as plain text, i.e. the source to render.
const source = plainContentToString(props.block.content).trim();
// Your own rendering, returning a preview element or an error for invalid
// source - the example below renders CSV to a table.
const { preview, error } = renderMySource(source);

return (
  <SourceBlockWithPreview
    block={props.block}
    editor={props.editor}
    contentRef={props.contentRef}
    source={source}
    // The last successfully rendered preview, or `undefined` - an errored
    // source then shows the error state instead of an empty preview.
    preview={preview}
    // Shown below the source in the popup while editing.
    error={error}
  />
);

还有一些 prop 可以自定义各种状态:errorPreview 用于替代预览显示紧凑的错误状态,emptySourcePlaceholder 用于源代码为空时的状态(传入字符串可以自定义默认占位符的文本,传入元素——例如带有自定义图标的导出组件 PreviewPlaceholder——则可以完全替换默认占位符),以及 sourcePlaceholder 用于设置弹出窗口输入框的占位符。完整列表请参见 SourceWithPreviewProps 类型。

3. spec 的 meta,用于启用弹出窗口:

const createMyBlockSpec = createReactBlockSpec(createMyBlockConfig, {
  meta: {
    code: true,
    // Marks the block as rendering a preview with an editable source popup.
    hasPreview: true,
    // What Enter does while the popup is open: "enter" inserts a newline
    // (multiline sources, like diagrams), "shift+enter" closes the popup
    // (single-line sources, like math).
    hardBreakShortcut: "enter",
  },
  render: MyBlockPreview,
});

由于块使用 "plain" 内容,因此你还可以在弹出窗口中为源代码添加语法高亮(数学和图表块就是这样做的):在 meta 中添加一个返回源代码语言的 highlight 回调,然后将语法高亮扩展添加到编辑器中。

自定义内联内容

内联内容的工作方式相同,但有两个区别:组件接收内联内容的渲染 props(nodegetPos),并且对于 "plain" 内联内容,源代码已经是一个纯字符串:

<SourceInlineContentWithPreview
  editor={props.editor}
  node={props.node}
  getPos={props.getPos}
  contentRef={props.contentRef}
  source={props.inlineContent.content.trim()}
  preview={preview}
  error={error}
/>

spec 通过 createReactInlineContentSpec 创建,并通过 meta: { code: true, hasPreview: true } 选择启用。与块不同——块通过点击切换弹出窗口——内联内容会在选区位于其源代码内部时准确地打开弹出窗口,因此它在被选中时始终会显示。

示例

一个同时实现了两者的完整示例——CSV 表格块和颜色标记内联内容:

@blocknote/math-block@blocknote/diagram-block 包是同一模式的生产级实现。