Loading...

功能特性

  • 文本建议:以内联标注形式添加文本标记建议
  • 区块建议:为整个内容区块创建建议
  • 状态追踪:追踪建议状态和用户交互
  • 撤销/重做支持:完整支持建议变更的撤销/重做
  • 讨论集成:与讨论插件协同工作实现完整协作

套件使用

安装

最快捷的添加建议功能方式是使用 SuggestionKit,它包含预配置的 SuggestionPlugin 及相关组件,以及它们的 Plate UI 组件。

添加套件

import { createPlateEditor } from 'platejs/react';
import { SuggestionKit } from '@/components/editor/plugins/suggestion-kit';
 
const editor = createPlateEditor({
  plugins: [
    // ...其他插件,
    ...SuggestionKit,
  ],
});

手动配置

安装

pnpm add @platejs/suggestion

扩展建议插件

创建带有状态管理扩展配置的建议插件:

import {
  type ExtendConfig,
  type Path,
  isSlateEditor,
  isSlateElement,
  isSlateString,
} from 'platejs';
import {
  type BaseSuggestionConfig,
  BaseSuggestionPlugin,
} from '@platejs/suggestion';
import { createPlatePlugin, toTPlatePlugin } from 'platejs/react';
import { BlockSuggestion } from '@/components/ui/block-suggestion';
import { SuggestionLeaf } from '@/components/ui/suggestion-node';
 
export type SuggestionConfig = ExtendConfig<
  BaseSuggestionConfig,
  {
    activeId: string | null;
    hoverId: string | null;
    uniquePathMap: Map<string, Path>;
  }
>;
 
export const suggestionPlugin = toTPlatePlugin<SuggestionConfig>(
  BaseSuggestionPlugin,
  ({ editor }) => ({
    options: {
      activeId: null,
      currentUserId: 'alice', // 设置当前用户ID
      hoverId: null,
      uniquePathMap: new Map(),
    },
    render: {
      node: SuggestionLeaf,
      belowRootNodes: ({ api, element }) => {
        if (!api.suggestion!.isBlockSuggestion(element)) {
          return null;
        }
 
        return <BlockSuggestion element={element} />;
      },
    },
  })
);
  • options.activeId:当前活跃建议ID,用于视觉高亮
  • options.currentUserId:创建建议的当前用户ID
  • options.hoverId:当前悬停建议ID,用于悬停效果
  • options.uniquePathMap:追踪建议解析唯一路径的映射表
  • render.node:指定 SuggestionLeaf 渲染建议文本标记
  • render.belowRootNodes:为区块级建议渲染 BlockSuggestion

添加点击处理器

添加点击处理以管理活跃建议状态:

export const suggestionPlugin = toTPlatePlugin<SuggestionConfig>(
  BaseSuggestionPlugin,
  ({ editor }) => ({
    handlers: {
      // 当点击建议外部时取消活跃建议
      onClick: ({ api, event, setOption, type }) => {
        let leaf = event.target as HTMLElement;
        let isSet = false;
 
        const unsetActiveSuggestion = () => {
          setOption('activeId', null);
          isSet = true;
        };
 
        if (!isSlateString(leaf)) unsetActiveSuggestion();
 
        while (
          leaf.parentElement &&
          !isSlateElement(leaf.parentElement) &&
          !isSlateEditor(leaf.parentElement)
        ) {
          if (leaf.classList.contains(`slate-${type}`)) {
            const suggestionEntry = api.suggestion!.node({ isText: true });
 
            if (!suggestionEntry) {
              unsetActiveSuggestion();
              break;
            }
 
            const id = api.suggestion!.nodeId(suggestionEntry[0]);
            setOption('activeId', id ?? null);
            isSet = true;
            break;
          }
 
          leaf = leaf.parentElement;
        }
 
        if (!isSet) unsetActiveSuggestion();
      },
    },
    // ... 之前的选项和渲染配置
  })
);

点击处理器追踪当前活跃建议:

  • 检测建议点击:遍历DOM查找建议元素
  • 设置活跃状态:点击建议时更新 activeId
  • 清除状态:点击建议外部时取消 activeId
  • 视觉反馈:在建议组件中启用悬停/活跃样式

添加插件

import { createPlateEditor, createPlatePlugin } from 'platejs/react';
import { SuggestionLineBreak } from '@/components/ui/suggestion-node';
 
const suggestionLineBreakPlugin = createPlatePlugin({
  key: 'suggestionLineBreak',
  render: { belowNodes: SuggestionLineBreak as any },
});
 
const editor = createPlateEditor({
  plugins: [
    // ...其他插件,
    suggestionPlugin,
    suggestionLineBreakPlugin,
  ],
});

启用建议模式

使用插件API控制建议模式:

import { useEditorRef, usePluginOption } from 'platejs/react';
 
function SuggestionToolbar() {
  const editor = useEditorRef();
  const isSuggesting = usePluginOption(suggestionPlugin, 'isSuggesting');
 
  const toggleSuggesting = () => {
    editor.setOption(suggestionPlugin, 'isSuggesting', !isSuggesting);
  };
 
  return (
    <button onClick={toggleSuggesting}>
      {isSuggesting ? '停止建议' : '开始建议'}
    </button>
  );
}

添加工具栏按钮

您可以在工具栏中添加 SuggestionToolbarButton 来切换编辑器的建议模式。

讨论集成

建议插件与讨论插件协同工作实现完整协作:

const editor = createPlateEditor({
  plugins: [
    // ...其他插件,
    discussionPlugin,
    suggestionPlugin.configure({
      options: {
        currentUserId: 'alice',
      },
    }),
    suggestionLineBreakPlugin,
  ],
});

键盘快捷键

KeyDescription
Cmd + Shift + S

在选中文本上添加建议

Plate Plus

插件

SuggestionPlugin

用于创建和管理文本及区块建议的插件,具有状态追踪和讨论集成功能。

Options

  • currentUserId string | null

    创建建议的当前用户ID。正确归属建议所必需。

  • isSuggesting boolean

    编辑器当前是否处于建议模式。内部用于追踪状态。

API

api.suggestion.dataList

从文本节点获取建议数据。

Parameters

Collapse all
  • node TSuggestionText

    建议文本节点。

ReturnsTInlineSuggestionData[]

    建议数据数组。

api.suggestion.isBlockSuggestion

检查节点是否为区块建议元素。

Parameters

Collapse all
  • node TElement

    要检查的节点。

Returnsnode is TSuggestionElement

    是否为区块建议。

api.suggestion.node

获取建议节点条目。

OptionsEditorNodesOptions & { id?: string; isText?: boolean }

    查找节点的选项。

ReturnsNodeEntry<TSuggestionElement | TSuggestionText> | undefined

    找到的建议节点条目。

api.suggestion.nodeId

从节点获取建议ID。

Parameters

Collapse all
  • node TElement | TSuggestionText

    要获取ID的节点。

Returnsstring | undefined

    找到的建议ID。

api.suggestion.nodes

获取所有匹配选项的建议节点条目。

OptionsEditorNodesOptions

    查找节点的选项。

ReturnsNodeEntry<TElement | TSuggestionText>[]

    建议节点条目数组。

api.suggestion.suggestionData

从节点获取建议数据。

Parameters

Collapse all
  • node TElement | TSuggestionText

    要获取建议数据的节点。

ReturnsTInlineSuggestionData | TSuggestionElement['suggestion'] | undefined

    找到的建议数据。

api.suggestion.withoutSuggestions

在执行函数时临时禁用建议。

Parameters

Collapse all
  • fn () => void

    要执行的函数。

类型

TSuggestionText

可包含建议的文本节点。

Attributes

Collapse all
  • suggestion optional boolean

    是否为建议。

  • suggestion_<id> optional TInlineSuggestionData

    建议数据。单个文本节点可包含多个建议。

TSuggestionElement

包含建议元数据的区块元素。

Attributes

Collapse all
  • suggestion TSuggestionData

    区块级建议数据,包括类型、用户和时间信息。

TInlineSuggestionData

内联文本建议的数据结构。

Attributes

Collapse all
  • id string

    建议的唯一标识符。

  • userId string

    创建建议的用户ID。

  • createdAt number

    建议创建的时间戳。

  • type 'insert' | 'remove' | 'update'

    建议操作类型。

  • newProperties optional object

    对于更新建议,建议的新标记属性。

  • properties optional object

    对于更新建议,先前的标记属性。

TSuggestionData

区块级建议的数据结构。

Attributes

Collapse all
  • id string

    建议的唯一标识符。

  • userId string

    创建建议的用户ID。

  • createdAt number

    建议创建的时间戳。

  • type 'insert' | 'remove'

    区块建议操作类型。

  • isLineBreak optional boolean

    该建议是否代表换行符插入。