Files
components/demo.tsx
'use client';

import * as React from 'react';

import { Plate, usePlateEditor } from 'platejs/react';

import { EditorKit } from '@/components/editor/editor-kit';
import { Editor, EditorContainer } from '@/components/ui/editor';

import { DEMO_VALUES } from './values/demo-values';

export default function Demo({ id }: { id: string }) {
  const editor = usePlateEditor({
    plugins: EditorKit,
    value: DEMO_VALUES[id],
  });

  return (
    <Plate editor={editor}>
      <EditorContainer variant="demo">
        <Editor />
      </EditorContainer>
    </Plate>
  );
}
Adding and displaying comments within content.
discussion-demo
discussion-demo

功能特点

  • 用户管理: 存储和管理带有头像和名称的用户数据
  • 讨论线程: 管理带有评论的讨论数据结构
  • 当前用户跟踪: 跟踪当前活跃用户以进行协作
  • 数据存储: 用于存储协作状态的纯 UI 插件
  • 选择器 API: 通过插件选择器轻松访问用户数据

Kit 使用

安装

添加讨论功能最快的方法是使用 DiscussionKit,它包含预配置的 discussionPlugin 及其 Plate UI 组件。

'use client';

import type { TComment } from '@/components/ui/comment';

import { createPlatePlugin } from 'platejs/react';

import { BlockDiscussion } from '@/components/ui/block-discussion';

export interface TDiscussion {
  id: string;
  comments: TComment[];
  createdAt: Date;
  isResolved: boolean;
  userId: string;
  documentContent?: string;
}

const discussionsData: TDiscussion[] = [
  {
    id: 'discussion1',
    comments: [
      {
        id: 'comment1',
        contentRich: [
          {
            children: [
              {
                text: 'Comments are a great way to provide feedback and discuss changes.',
              },
            ],
            type: 'p',
          },
        ],
        createdAt: new Date(Date.now() - 600_000),
        discussionId: 'discussion1',
        isEdited: false,
        userId: 'charlie',
      },
      {
        id: 'comment2',
        contentRich: [
          {
            children: [
              {
                text: 'Agreed! The link to the docs makes it easy to learn more.',
              },
            ],
            type: 'p',
          },
        ],
        createdAt: new Date(Date.now() - 500_000),
        discussionId: 'discussion1',
        isEdited: false,
        userId: 'bob',
      },
    ],
    createdAt: new Date(),
    documentContent: 'comments',
    isResolved: false,
    userId: 'charlie',
  },
  {
    id: 'discussion2',
    comments: [
      {
        id: 'comment1',
        contentRich: [
          {
            children: [
              {
                text: 'Nice demonstration of overlapping annotations with both comments and suggestions!',
              },
            ],
            type: 'p',
          },
        ],
        createdAt: new Date(Date.now() - 300_000),
        discussionId: 'discussion2',
        isEdited: false,
        userId: 'bob',
      },
      {
        id: 'comment2',
        contentRich: [
          {
            children: [
              {
                text: 'This helps users understand how powerful the editor can be.',
              },
            ],
            type: 'p',
          },
        ],
        createdAt: new Date(Date.now() - 200_000),
        discussionId: 'discussion2',
        isEdited: false,
        userId: 'charlie',
      },
    ],
    createdAt: new Date(),
    documentContent: 'overlapping',
    isResolved: false,
    userId: 'bob',
  },
];

const avatarUrl = (seed: string) =>
  `https://api.dicebear.com/9.x/glass/svg?seed=${seed}`;

const usersData: Record<
  string,
  { id: string; avatarUrl: string; name: string; hue?: number }
> = {
  alice: {
    id: 'alice',
    avatarUrl: avatarUrl('alice6'),
    name: 'Alice',
  },
  bob: {
    id: 'bob',
    avatarUrl: avatarUrl('bob4'),
    name: 'Bob',
  },
  charlie: {
    id: 'charlie',
    avatarUrl: avatarUrl('charlie2'),
    name: 'Charlie',
  },
};

// This plugin is purely UI. It's only used to store the discussions and users data
export const discussionPlugin = createPlatePlugin({
  key: 'discussion',
  options: {
    currentUserId: 'alice',
    discussions: discussionsData,
    users: usersData,
  },
})
  .configure({
    render: { aboveNodes: BlockDiscussion },
  })
  .extendSelectors(({ getOption }) => ({
    currentUser: () => getOption('users')[getOption('currentUserId')],
    user: (id: string) => getOption('users')[id],
  }));

export const DiscussionKit = [discussionPlugin];

添加 Kit

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

手动使用

安装

pnpm add @platejs/comment @platejs/suggestion

创建插件

import { createPlatePlugin } from 'platejs/react';
import { BlockDiscussion } from '@/components/ui/block-discussion';
 
export interface TDiscussion {
  id: string;
  comments: TComment[];
  createdAt: Date;
  isResolved: boolean;
  userId: string;
  documentContent?: string;
}
 
const usersData = {
  alice: {
    id: 'alice',
    avatarUrl: 'https://api.dicebear.com/9.x/glass/svg?seed=alice6',
    name: 'Alice',
  },
  bob: {
    id: 'bob', 
    avatarUrl: 'https://api.dicebear.com/9.x/glass/svg?seed=bob4',
    name: 'Bob',
  },
};
 
export const discussionPlugin = createPlatePlugin({
  key: 'discussion',
  options: {
    currentUserId: 'alice',
    discussions: [],
    users: usersData,
  },
})
  .configure({
    render: { aboveNodes: BlockDiscussion },
  })
  .extendSelectors(({ getOption }) => ({
    currentUser: () => getOption('users')[getOption('currentUserId')],
    user: (id: string) => getOption('users')[id],
  }));
  • options.currentUserId: 当前活跃用户的 ID
  • options.discussions: 讨论数据结构数组
  • options.users: 将用户 ID 映射到用户数据的对象
  • render.aboveNodes: 在节点上方渲染 BlockDiscussion 用于讨论 UI
  • selectors.currentUser: 获取当前用户数据
  • selectors.user: 通过 ID 获取用户数据

添加插件

import { createPlateEditor } from 'platejs/react';
 
const editor = createPlateEditor({
  plugins: [
    // ...其他插件,
    discussionPlugin,
  ],
});

插件

discussionPlugin

用于管理包括用户和讨论数据在内的协作状态的纯 UI 插件。

Options

Collapse all

    协作会话中当前活跃用户的 ID。

    包含评论和元数据的讨论对象数组。

    将用户 ID 映射到包括名称和头像在内的用户信息的对象。

选择器

currentUser

获取当前用户数据。

ReturnsUserData

Collapse all

    当前用户的数据,包括 id、name 和 avatarUrl。

user

通过 ID 获取用户数据。

Parameters

Collapse all

    要查找的用户 ID。

ReturnsUserData | undefined

Collapse all

    如果找到则返回用户数据,否则返回 undefined。

类型

TDiscussion

包含评论和元数据的讨论数据结构。

Attributes

Collapse all

    讨论的唯一标识符。

    讨论线程中的评论数组。

    讨论创建的时间。

    讨论是否已解决。

    创建讨论的用户 ID。

    与此讨论相关的文档内容。

UserData

用于协作的用户信息结构。

Attributes

Collapse all

    用户的唯一标识符。

    用户的显示名称。

    用户头像图片的 URL。

    用于用户识别的可选颜色色调。