文档
建议菜单

建议菜单

当用户输入触发字符时,会出现建议菜单,触发字符后的文本用于过滤菜单项。

斜杠菜单

斜杠菜单是一个建议菜单,使用 / 字符打开(或点击 块侧边菜单 中的 + 按钮时打开)。

image

更改斜杠菜单项

你可以更改斜杠菜单中的项。下面的演示添加了一个插入新块的项,其中包含加粗的“Hello World”。

import {
  BlockNoteEditor,
  filterSuggestionItems,
  insertOrUpdateBlock,
} from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import {
  DefaultReactSuggestionItem,
  getDefaultReactSlashMenuItems,
  SuggestionMenuController,
  useCreateBlockNote,
} from "@blocknote/react";
import { HiOutlineGlobeAlt } from "react-icons/hi";
 
// Custom Slash Menu item to insert a block after the current one.
const insertHelloWorldItem = (editor: BlockNoteEditor) => ({
  title: "Insert Hello World",
  onItemClick: () =>
    // If the block containing the text caret is empty, `insertOrUpdateBlock`
    // changes its type to the provided block. Otherwise, it inserts the new
    // block below and moves the text caret to it. We use this function with
    // a block containing 'Hello World' in bold.
    insertOrUpdateBlock(editor, {
      type: "paragraph",
      content: [{ type: "text", text: "Hello World", styles: { bold: true } }],
    }),
  aliases: ["helloworld", "hw"],
  group: "Other",
  icon: <HiOutlineGlobeAlt size={18} />,
  subtext: "Used to insert a block with 'Hello World' below.",
});
 
// List containing all default Slash Menu Items, as well as our custom one.
const getCustomSlashMenuItems = (
  editor: BlockNoteEditor,
): DefaultReactSuggestionItem[] => [
  ...getDefaultReactSlashMenuItems(editor),
  insertHelloWorldItem(editor),
];
 
export default function App() {
  // Creates a new editor instance.
  const editor = useCreateBlockNote({
    initialContent: [
      {
        type: "paragraph",
        content: "Welcome to this demo!",
      },
      {
        type: "paragraph",
        content: "Press the '/' key to open the Slash Menu",
      },
      {
        type: "paragraph",
        content: "Notice the new 'Insert Hello World' item - try it out!",
      },
      {
        type: "paragraph",
      },
    ],
  });
 
  // Renders the editor instance.
  return (
    <BlockNoteView editor={editor} slashMenu={false}>
      <SuggestionMenuController
        triggerCharacter={"/"}
        // Replaces the default Slash Menu items with our custom ones.
        getItems={async (query) =>
          filterSuggestionItems(getCustomSlashMenuItems(editor), query)
        }
      />
    </BlockNoteView>
  );
}
 

斜杠菜单项是具有以下字段的对象:

type DefaultSuggestionItem = {
  title: string;
  onItemClick: () => void;
  subtext?: string;
  badge?: string;
  aliases?: string[];
  group?: string;
};

title: 项的标题。

onItemClick: 选择该项时调用的回调函数。

subtext: 项的副标题。

badge: 在项内徽章中显示的文本。用于显示该项的键盘快捷键。

aliases:title 外的该项其他名称,用于基于用户查询过滤项。

group: 项所属的组。属于同一组的项在菜单中用分隔线分隔。为确保项被正确分组,请确保它们在传递给斜杠菜单的项数组中是连续的。

创建你的项后,你还需要对 BlockNoteView 进行一些更改,以将该项添加到斜杠菜单。

BlockNoteView 传递 slashMenu={false} 告诉 BlockNote 不显示默认斜杠菜单。添加带有 triggerCharacter={"/"} 和自定义 getItems 函数的 SuggestionMenuController 则告诉 BlockNote 显示一个自定义项的菜单。

getItems 应基于用户输入的 query(用户在触发字符后输入的内容)返回需要显示在斜杠菜单中的项。在此示例中,我们只是将“Hello World”项添加到默认斜杠菜单项列表,并使用 filterSuggestionItems 根据用户查询过滤完整项列表。

替换斜杠菜单组件

你可以用自己的 React 组件替换斜杠菜单中使用的组件,下面的演示展示了这一点。

import "@blocknote/core/fonts/inter.css";
import {
  DefaultReactSuggestionItem,
  SuggestionMenuController,
  SuggestionMenuProps,
  useCreateBlockNote,
} from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
 
import "./styles.css";
 
// Custom component to replace the default Slash Menu.
function CustomSlashMenu(
  props: SuggestionMenuProps<DefaultReactSuggestionItem>,
) {
  return (
    <div className={"slash-menu"}>
      {props.items.map((item, index) => (
        <div
          className={`slash-menu-item ${
            props.selectedIndex === index ? "selected" : ""
          }`}
          onClick={() => {
            props.onItemClick?.(item);
          }}
        >
          {item.title}
        </div>
      ))}
    </div>
  );
}
 
export default function App() {
  // Creates a new editor instance.
  const editor = useCreateBlockNote({
    initialContent: [
      {
        type: "paragraph",
        content: "Welcome to this demo!",
      },
      {
        type: "paragraph",
        content: "Press the '/' key to open the Slash Menu",
      },
      {
        type: "paragraph",
        content: "It's been replaced with a custom component",
      },
      {
        type: "paragraph",
      },
    ],
  });
 
  // Renders the editor instance.
  return (
    <BlockNoteView editor={editor} slashMenu={false}>
      <SuggestionMenuController
        triggerCharacter={"/"}
        suggestionMenuComponent={CustomSlashMenu}
      />
    </BlockNoteView>
  );
}
 

同样,我们添加了带有 triggerCharacter={"/"}SuggestionMenuController 组件,并设置 slashMenu={false} 来替换默认斜杠菜单。

现在,我们还将一个组件传递给它的 suggestionMenuComponent 属性。传递的 suggestionMenuComponent 负责渲染过滤后的项。SuggestionMenuController 控制它的位置和可见性(触发字符下方),并且还决定显示哪些项(使用之前见过的可选 getItems 属性)。

创建额外的建议菜单

你可以在编辑器中添加额外的建议菜单,触发字符可以为任意字符。下面的演示添加了一个示例建议菜单,用于提及,使用 @ 字符打开。

import {
  BlockNoteSchema,
  defaultInlineContentSpecs,
  filterSuggestionItems,
} from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import {
  DefaultReactSuggestionItem,
  SuggestionMenuController,
  useCreateBlockNote,
} from "@blocknote/react";
 
import { Mention } from "./Mention.js";
 
// Our schema with inline content specs, which contain the configs and
// implementations for inline content  that we want our editor to use.
const schema = BlockNoteSchema.create({
  inlineContentSpecs: {
    // Adds all default inline content.
    ...defaultInlineContentSpecs,
    // Adds the mention tag.
    mention: Mention,
  },
});
 
// Function which gets all users for the mentions menu.
const getMentionMenuItems = (
  editor: typeof schema.BlockNoteEditor,
): DefaultReactSuggestionItem[] => {
  const users = ["Steve", "Bob", "Joe", "Mike"];
 
  return users.map((user) => ({
    title: user,
    onItemClick: () => {
      editor.insertInlineContent([
        {
          type: "mention",
          props: {
            user,
          },
        },
        " ", // add a space after the mention
      ]);
    },
  }));
};
 
export function App() {
  const editor = useCreateBlockNote({
    schema,
    initialContent: [
      {
        type: "paragraph",
        content: "Welcome to this demo!",
      },
      {
        type: "paragraph",
        content: [
          {
            type: "mention",
            props: {
              user: "Steve",
            },
          },
          {
            type: "text",
            text: " <- This is an example mention",
            styles: {},
          },
        ],
      },
      {
        type: "paragraph",
        content: "Press the '@' key to open the mentions menu and add another",
      },
      {
        type: "paragraph",
      },
    ],
  });
 
  return (
    <BlockNoteView editor={editor}>
      {/* Adds a mentions menu which opens with the "@" key */}
      <SuggestionMenuController
        triggerCharacter={"@"}
        getItems={async (query) =>
          // Gets the mentions menu items
          filterSuggestionItems(getMentionMenuItems(editor), query)
        }
      />
    </BlockNoteView>
  );
}
 
export default App;
 

以同样的方式更改新建议菜单中的项,或更改用于渲染的组件,方法同 斜杠菜单。有关提及元素的更多信息,请参阅 自定义内联内容

其他功能

BlockNote 提供了一些用于建议菜单的其他功能,可能适合你的使用场景。

编程式打开建议菜单

虽然建议菜单通常在用户按下触发字符时打开,但你也可以通过代码打开它们。为此,你可以使用编辑器的以下方法:

openSuggestionMenu(triggerCharacter: string): void;
 
// 用法示例
editor.openSuggestionMenu("/");

等待查询

你可能希望延迟显示建议菜单,除非确定用户确实想打开菜单,而不仅仅是输入了触发字符。在这种情况下,应使用 SuggestionMenuControllerminQueryLength 属性,该属性接受一个数字。

该数字表示用户查询需要达到的字符数,菜单才会显示。当大于 0 时,如果用户在触发字符后立即输入空格,也会阻止菜单显示。