[lexical-react][lexical-yjs] Feature: Support custom cursor sync in CollaborationPlugin (#7486)

This commit is contained in:
Simone Cervini
2025-04-24 21:07:10 +02:00
committed by GitHub
parent ecb9e8351b
commit 1b08c62441
2 changed files with 25 additions and 1 deletions

View File

@ -19,6 +19,7 @@ import {
createBinding,
ExcludedProperties,
Provider,
SyncCursorPositionsFn,
} from '@lexical/yjs';
import {LexicalEditor} from 'lexical';
import {useEffect, useRef, useState} from 'react';
@ -46,6 +47,7 @@ type Props = {
excludedProperties?: ExcludedProperties;
// `awarenessData` parameter allows arbitrary data to be added to the awareness.
awarenessData?: object;
syncCursorPositionsFn?: SyncCursorPositionsFn;
};
export function CollaborationPlugin({
@ -58,6 +60,7 @@ export function CollaborationPlugin({
initialEditorState,
excludedProperties,
awarenessData,
syncCursorPositionsFn,
}: Props): JSX.Element {
const isBindingInitialized = useRef(false);
const isProviderInitialized = useRef(false);
@ -145,6 +148,7 @@ export function CollaborationPlugin({
setDoc={setDoc}
shouldBootstrap={shouldBootstrap}
yjsDocMap={yjsDocMap}
syncCursorPositionsFn={syncCursorPositionsFn}
/>
);
}
@ -163,6 +167,7 @@ function YjsCollaborationCursors({
collabContext,
binding,
setDoc,
syncCursorPositionsFn,
}: {
editor: LexicalEditor;
id: string;
@ -177,6 +182,7 @@ function YjsCollaborationCursors({
initialEditorState?: InitialEditorStateType | undefined;
awarenessData?: object;
collabContext: CollaborationContextType;
syncCursorPositionsFn?: SyncCursorPositionsFn;
}) {
const cursors = useYjsCollaboration(
editor,
@ -191,6 +197,7 @@ function YjsCollaborationCursors({
cursorsContainerRef,
initialEditorState,
awarenessData,
syncCursorPositionsFn,
);
collabContext.clientID = binding.clientID;

View File

@ -413,13 +413,30 @@ function getCollabNodeAndOffset(
export type SyncCursorPositionsFn = (
binding: Binding,
provider: Provider,
options?: SyncCursorPositionsOptions,
) => void;
export type SyncCursorPositionsOptions = {
getAwarenessStates?: (
binding: Binding,
provider: Provider,
) => Map<number, UserState>;
};
function getAwarenessStatesDefault(
_binding: Binding,
provider: Provider,
): Map<number, UserState> {
return provider.awareness.getStates();
}
export function syncCursorPositions(
binding: Binding,
provider: Provider,
options?: SyncCursorPositionsOptions,
): void {
const awarenessStates = Array.from(provider.awareness.getStates());
const {getAwarenessStates = getAwarenessStatesDefault} = options ?? {};
const awarenessStates = Array.from(getAwarenessStates(binding, provider));
const localClientID = binding.clientID;
const cursors = binding.cursors;
const editor = binding.editor;