┌────────────────────────────────────────────────────────────────────────┐ │ 📄 shadcn/directory/udecode/plate/(plugins)/(collaboration)/comment.cn │ └────────────────────────────────────────────────────────────────────────┘
╔══════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
title: 评论功能 docs:
最快捷的添加评论功能方式是使用 CommentKit,它包含预配置的 commentPlugin 和相关组件以及它们的 Plate UI 组件。
CommentLeaf: 渲染评论文本标记BlockDiscussion: 渲染集成评论功能的讨论界面import { createPlateEditor } from 'platejs/react';
import { CommentKit } from '@/components/editor/plugins/comment-kit';
const editor = createPlateEditor({
plugins: [
// ...其他插件
...CommentKit,
],
});
</Steps>
npm install @platejs/comment
创建带有状态管理扩展配置的评论插件:
import {
type ExtendConfig,
type Path,
isSlateString,
} from 'platejs';
import {
type BaseCommentConfig,
BaseCommentPlugin,
getDraftCommentKey,
} from '@platejs/comment';
import { toTPlatePlugin } from 'platejs/react';
import { CommentLeaf } from '@/components/ui/comment-node';
type CommentConfig = ExtendConfig<
BaseCommentConfig,
{
activeId: string | null;
commentingBlock: Path | null;
hoverId: string | null;
uniquePathMap: Map<string, Path>;
}
>;
export const commentPlugin = toTPlatePlugin<CommentConfig>(
BaseCommentPlugin,
({ editor }) => ({
options: {
activeId: null,
commentingBlock: null,
hoverId: null,
uniquePathMap: new Map(),
},
render: {
node: CommentLeaf,
},
})
);
options.activeId: 当前激活评论ID,用于视觉高亮options.commentingBlock: 当前被评论区块的路径options.hoverId: 当前悬停评论ID,用于悬停效果options.uniquePathMap: 追踪评论解析唯一路径的映射表render.node: 指定 CommentLeaf 来渲染评论文本标记添加点击处理来管理激活评论状态:
export const commentPlugin = toTPlatePlugin<CommentConfig>(
BaseCommentPlugin,
({ editor }) => ({
handlers: {
// 点击评论标记时设置激活评论
onClick: ({ api, event, setOption, type }) => {
let leaf = event.target as HTMLElement;
let isSet = false;
const unsetActiveComment = () => {
setOption('activeId', null);
isSet = true;
};
if (!isSlateString(leaf)) unsetActiveComment();
while (leaf.parentElement) {
if (leaf.classList.contains(`slate-${type}`)) {
const commentsEntry = api.comment.node();
if (!commentsEntry) {
unsetActiveComment();
break;
}
const id = api.comment.nodeId(commentsEntry[0]);
setOption('activeId', id ?? null);
isSet = true;
break;
}
leaf = leaf.parentElement;
}
if (!isSet) unsetActiveComment();
},
},
// ... 之前的options和render配置
})
);
点击处理器追踪当前激活的评论:
activeIdactiveId扩展setDraft转换以增强功能:
export const commentPlugin = toTPlatePlugin<CommentConfig>(
BaseCommentPlugin,
({ editor }) => ({
// ... 之前的配置
})
)
.extendTransforms(
({
editor,
setOption,
tf: {
comment: { setDraft },
},
}) => ({
setDraft: () => {
if (editor.api.isCollapsed()) {
editor.tf.select(editor.api.block()![1]);
}
setDraft();
editor.tf.collapse();
setOption('activeId', getDraftCommentKey());
setOption('commentingBlock', editor.selection!.focus.path.slice(0, 1));
},
})
)
.configure({
node: { component: CommentLeaf },
shortcuts: {
setDraft: { keys: 'mod+shift+m' },
},
});
您可以在工具栏中添加CommentToolbarButton来为选中文本添加评论。
import { createPlateEditor } from 'platejs/react';
const editor = createPlateEditor({
plugins: [
// ...其他插件
commentPlugin,
],
});
评论插件可与讨论插件配合实现完整协作:
import { discussionPlugin } from '@/components/editor/plugins/discussion-kit';
const editor = createPlateEditor({
plugins: [
// ...其他插件
discussionPlugin,
commentPlugin,
],
});
</Steps>
CommentPlugin用于创建和管理文本评论的插件,具有状态追踪和讨论集成功能。
<API name="CommentPlugin"> <APIOptions> <APIItem name="activeId" type="string | null"> 当前激活评论ID,用于视觉高亮。内部用于状态追踪。 </APIItem> <APIItem name="commentingBlock" type="Path | null"> 当前被评论区块的路径。 </APIItem> <APIItem name="hoverId" type="string | null"> 当前悬停评论ID,用于悬停效果。 </APIItem> <APIItem name="uniquePathMap" type="Map<string, Path>"> 追踪评论解析唯一路径的映射表。 </APIItem> </APIOptions> </API>api.comment.has检查编辑器中是否存在指定ID的评论。
<API name="has"> <APIParameters> <APIItem name="options" type="{ id: string }"> 包含要检查评论ID的选项。 </APIItem> </APIParameters> <APIReturns type="boolean"> 评论是否存在。 </APIReturns> </API>api.comment.node获取评论节点entry。
<API name="node"> <APIOptions type="EditorNodesOptions & { id?: string; isDraft?: boolean }" optional> 查找节点的选项。 </APIOptions> <APIReturns type="NodeEntry<TCommentText> | undefined"> 找到的评论节点entry。 </APIReturns> </API>api.comment.nodeId从叶子节点获取评论ID。
<API name="nodeId"> <APIParameters> <APIItem name="leaf" type="TCommentText"> 评论叶子节点。 </APIItem> </APIParameters> <APIReturns type="string | undefined"> 找到的评论ID。 </APIReturns> </API>api.comment.nodes获取所有匹配选项的评论节点entry。
<API name="nodes"> <APIOptions type="EditorNodesOptions & { id?: string; isDraft?: boolean }" optional> 查找节点的选项。 </APIOptions> <APIReturns type="NodeEntry<TCommentText>[]"> 评论节点entry数组。 </APIReturns> </API>tf.comment.removeMark从当前选区或指定位置移除评论标记。
<API name="removeMark" />tf.comment.setDraft在当前选区设置草稿评论标记。
<API name="setDraft"> <APIOptions type="SetNodesOptions" optional> 设置草稿评论的选项。 </APIOptions> </API>tf.comment.unsetMark从编辑器中取消设置指定ID的评论节点。
<API name="unsetMark"> <APIParameters> <APIItem name="options" type="{ id: string }"> 包含要取消评论ID的选项。 </APIItem> </APIParameters> </API>getCommentCount获取评论节点中非草稿评论的数量。
<API name="getCommentCount"> <APIParameters> <APIItem name="node" type="TCommentText"> 评论节点。 </APIItem> </APIParameters> <APIReturns type="number"> 评论数量。 </APIReturns> </API>getCommentKey基于提供的ID生成评论key。
<API name="getCommentKey"> <APIParameters> <APIItem name="id" type="string"> 评论ID。 </APIItem> </APIParameters> <APIReturns type="string"> 生成的评论key。 </APIReturns> </API>getCommentKeyId从评论key中提取评论ID。
<API name="getCommentKeyId"> <APIParameters> <APIItem name="key" type="string"> 评论key。 </APIItem> </APIParameters> <APIReturns type="string"> 提取的评论ID。 </APIReturns> </API>getCommentKeys返回给定节点中存在的评论key数组。
<API name="getCommentKeys"> <APIParameters> <APIItem name="node" type="TCommentText"> 要检查评论key的节点。 </APIItem> </APIParameters> <APIReturns type="string[]"> 评论key数组。 </APIReturns> </API>getDraftCommentKey获取草稿评论使用的key。
<API name="getDraftCommentKey"> <APIReturns type="string"> 草稿评论key。 </APIReturns> </API>isCommentKey检查给定key是否为评论key。
<API name="isCommentKey"> <APIParameters> <APIItem name="key" type="string"> 要检查的key。 </APIItem> </APIParameters> <APIReturns type="boolean"> 是否为评论key。 </APIReturns> </API>isCommentNodeById检查给定节点是否为指定ID的评论。
<API name="isCommentNodeById"> <APIParameters> <APIItem name="node" type="TNode"> 要检查的节点。 </APIItem> <APIItem name="id" type="string"> 评论ID。 </APIItem> </APIParameters> <APIReturns type="boolean"> 节点是否为指定ID的评论。 </APIReturns> </API>TCommentText可包含评论的文本节点。
<API name="TCommentText"> <APIAttributes> <APIItem name="comment" type="boolean" optional> 该文本节点是否包含评论。 </APIItem> <APIItem name="comment_<id>" type="boolean" optional> 按评论ID索引的评论数据。一个文本节点可包含多个评论。 </APIItem> </APIAttributes> </API>║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
╚══════════════════════════════════════════════════════════════════════════════════════════════╝