Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-ghosts-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@udecode/plate-yjs": minor
---

New plugin: yjs for collaboration.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- test
if: ${{ failure() }}
if: ${{ !github.event.pull_request.draft && failure() }}

steps:
- name: Notify
Expand Down
132 changes: 132 additions & 0 deletions apps/www/content/docs/collaboration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: Collaboration
description: Collaborate with others in a single document.
---

<Callout className="my-4">

**Backend Development in Progress** - Demo Unavailable.

</Callout>

<PackageInfo>

## Features

- The yjs plugin enables support for collaboration using [slate-yjs](https://docs.slate-yjs.dev/) and [Hocuspocus](https://docs.slate-yjs.dev/walkthroughs/collaboration-hocuspocus).

</PackageInfo>

## Frontend

### Installation

```bash
npm install @udecode/plate-yjs
```

### Usage

```tsx
import { createComboboxPlugin } from '@udecode/plate-combobox';
import { createMentionPlugin } from '@udecode/plate-mention';

const plugins = [
// ...otherPlugins,
createYjsPlugin({
options: {
hocuspocusProviderOptions: {
url, // hocuspocus url
name, // room name
},
},
}),
];
```

## Backend

Follow the backend instructions in [Hocuspocus docs](https://tiptap.dev/hocuspocus/getting-started).

## API

### createYjsPlugin

<APIOptions>
<APIItem name="cursorOptions" type="WithCursorsOptions<TCursorData>" optional>
Configuration for handling cursors in the editor. These options are passed
to the `withCursors` function.

- [WithCursorsOptions API](https://docs.slate-yjs.dev/api/slate-yjs-core/cursor-plugin#withcursors).

</APIItem>
<APIItem
name="hocuspocusProviderOptions"
type="HocuspocusProviderConfiguration"
required
>
Configuration for the Hocuspocus provider.

- [HocuspocusProviderConfiguration API](https://tiptap.dev/hocuspocus/provider/configuration).

</APIItem>
<APIItem name="yjsOptions" type="WithYjsOptions" optional>
Optional configuration for the Yjs integration. These options are passed to
the `withYjs` function.

- [WithYjsOptions API](https://docs.slate-yjs.dev/api/slate-yjs-core/yjs-plugin#withyjs).

</APIItem>
<APIItem name="disableCursors" type="boolean" optional>
A flag indicating whether to disable the cursor feature in the editor.
</APIItem>
</APIOptions>

### useYjsStore

<APIState>
<APIItem name="isConnected" type="boolean">
A flag indicating whether the editor is connected to the Hocuspocus server.
</APIItem>
<APIItem name="isSynced" type="boolean">
A flag indicating whether the editor is synced with the Hocuspocus server.
</APIItem>
</APIState>

### withTYjs

Higher-order function that wraps a Plate editor with Yjs support (`YjsEditorProps`), allowing for real-time collaborative editing.

### CursorEditorProps

Extends `YjsEditorProps` and [CursorEditor](https://docs.slate-yjs.dev/api/slate-yjs-core/cursor-plugin#cursoreditor).

### PlateYjsEditorProps

<APIAttributes>

Extends `CursorEditorProps`.

<APIItem name="yjs" type="object">

<APISubList>

<APISubListItem parent="yjs" name="provider" type="HocuspocusProvider">

The Hocuspocus provider instance.

</APISubListItem>

</APISubList>

</APIItem>

</APIAttributes>

### YHistoryEditorProps

Extends `YjsEditorProps`, [YHistoryEditor](https://docs.slate-yjs.dev/api/slate-yjs-core/history-plugin#yhistoryeditor).

### YjsEditorProps

[YjsEditor API](https://docs.slate-yjs.dev/api/slate-yjs-core/yjs-plugin#yjseditor).
1 change: 1 addition & 0 deletions apps/www/src/config/doc-to-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const plateOverrides = {
'basic-marks': 'plate-basic-marks',
'block-selection': 'plate-selection',
cloud: 'plate-cloud',
collaboration: 'plate-yjs',
combobox: 'plate-combobox',
comments: 'plate-comments',
dnd: 'plate-dnd',
Expand Down
5 changes: 5 additions & 0 deletions apps/www/src/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ export const docsConfig: DocsConfig = {
items: [],
label: 'Element',
},
{
title: 'Collaboration',
href: '/docs/collaboration',
items: [],
},
{
title: 'Combobox',
href: '/docs/combobox',
Expand Down
1 change: 1 addition & 0 deletions config/rollup.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const plugins = [
include: /node_modules/,
namedExports: {
'react-is': ['typeOf', 'isElement', 'isForwardRef', 'isValidElementType'],
'scheduler': ['unstable_runWithPriority', 'unstable_NormalPriority'],
},
}),

Expand Down
3 changes: 3 additions & 0 deletions packages/yjs/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__tests__
__test-utils__
__mocks__
12 changes: 12 additions & 0 deletions packages/yjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Plate yjs plugin

This package implements the yjs plugin for Plate and Hocuspocus.

## Documentation

Check out
[Collaboration](https://platejs.org/docs/collaboration).

## License

[MIT](../../LICENSE)
54 changes: 54 additions & 0 deletions packages/yjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@udecode/plate-yjs",
"version": "22.0.2",
"description": "Yjs plugin for Plate",
"license": "MIT",
"homepage": "https://plate.udecode.io",
"repository": {
"type": "git",
"url": "https://github.com/udecode/plate.git",
"directory": "packages/yjs"
},
"bugs": {
"url": "https://github.com/udecode/plate/issues"
},
"main": "dist/index.js",
"module": "dist/index.es.js",
"files": [
"dist"
],
"types": "dist/index.d.ts",
"scripts": {
"build": "yarn p:build",
"build:watch": "yarn p:build:watch",
"brl": "yarn p:brl",
"clean": "yarn p:clean",
"lint": "yarn p:lint",
"lint:fix": "yarn p:lint:fix",
"test": "yarn p:test",
"test:watch": "yarn p:test:watch",
"typecheck": "yarn p:typecheck"
},
"dependencies": {
"@hocuspocus/provider": "^2.2.1",
"@slate-yjs/core": "^1.0.1",
"@udecode/plate-common": "22.0.2",
"yjs": "^13.5.42"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"slate": ">=0.94.0",
"slate-history": ">=0.93.0",
"slate-react": ">=0.95.0"
},
"keywords": [
"plate",
"plugin",
"slate",
"yjs"
],
"publishConfig": {
"access": "public"
}
}
29 changes: 29 additions & 0 deletions packages/yjs/src/RenderAboveEditableYjs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { FC, ReactNode, useEffect } from 'react';
import { YjsEditor } from '@slate-yjs/core';
import { PlateEditor, usePlateEditorRef, Value } from '@udecode/plate-common';

import { PlateYjsEditorProps } from './withPlateYjs';
import { useYjsSelectors } from './yjsStore';

export const RenderAboveEditableYjs: FC<{ children: ReactNode }> = ({
children,
}) => {
const editor = usePlateEditorRef<Value, PlateEditor & PlateYjsEditorProps>();

const isSynced = useYjsSelectors.isSynced();

useEffect(() => {
editor.yjs.provider.connect();
return () => editor.yjs.provider.disconnect();
}, [editor.yjs.provider]);

useEffect(() => {
YjsEditor.connect(editor as any);
return () => YjsEditor.disconnect(editor as any);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editor.yjs.provider.awareness, editor.yjs.provider.document]);

if (!isSynced) return null;

return <>{children}</>;
};
45 changes: 45 additions & 0 deletions packages/yjs/src/createYjsPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { HocuspocusProviderConfiguration } from '@hocuspocus/provider';
import { WithCursorsOptions } from '@slate-yjs/core';
// eslint-disable-next-line import/no-unresolved
import { WithYjsOptions } from '@slate-yjs/core/dist/plugins/withYjs';
import {
createPluginFactory,
PlateEditor,
UnknownObject,
Value,
} from '@udecode/plate-common';

import { RenderAboveEditableYjs } from './RenderAboveEditableYjs';
import { PlateYjsEditorProps, withPlateYjs } from './withPlateYjs';

export type YjsPlugin<TCursorData extends UnknownObject = UnknownObject> = {
/**
* withCursors options
*/
cursorOptions?: WithCursorsOptions<TCursorData>;

/**
* HocuspocusProvider configuration
* @required
*/
hocuspocusProviderOptions?: HocuspocusProviderConfiguration;

/**
* withYjs options
*/
yjsOptions?: WithYjsOptions;

disableCursors?: boolean;
};

export const KEY_YJS = 'yjs';

export const createYjsPlugin = createPluginFactory<
YjsPlugin,
Value,
PlateEditor & PlateYjsEditorProps
>({
key: KEY_YJS,
withOverrides: withPlateYjs,
renderAboveEditable: RenderAboveEditableYjs,
});
11 changes: 11 additions & 0 deletions packages/yjs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @file Automatically generated by barrelsby.
*/

export * from './RenderAboveEditableYjs';
export * from './createYjsPlugin';
export * from './withPlateYjs';
export * from './withTCursors';
export * from './withTYHistory';
export * from './withTYjs';
export * from './yjsStore';
Loading