marshall-y-slate
Version:
Yjs bindings for Slate.
94 lines (82 loc) • 3.53 kB
text/typescript
import { useCallback, useEffect, useState } from 'react';
import { NodeEntry, Path, Range, Text } from 'slate';
import { Cursor, CursorInfo } from '../model';
import { relativePositionToAbsolutePosition } from '../cursor/utils';
import { CursorEditor } from './cursor-editor';
export const useCursors = (
editor: CursorEditor
): {
decorate: (entry: NodeEntry) => Range[];
cursors: Cursor[];
} => {
const [cursors, setCursorData] = useState<Cursor[]>([]);
useEffect(() => {
editor.awareness.on('update', () => {
const newCursorData = Array.from(editor.awareness.getStates())
.filter(([clientId]) => clientId !== editor.sharedType.doc?.clientID)
.map(([, awareness]) => {
let anchor = null;
let focus = null;
if (awareness.anchor) {
anchor = relativePositionToAbsolutePosition(
editor.sharedType,
awareness.anchor
);
}
if (awareness.focus) {
focus = relativePositionToAbsolutePosition(
editor.sharedType,
awareness.focus
);
}
return { anchor, focus, data: awareness };
})
.filter((cursor) => cursor.anchor && cursor.focus);
setCursorData(newCursorData as unknown as Cursor[]);
});
}, [editor]);
const decorate = useCallback(
([node, path]: NodeEntry) => {
const ranges: (Range & CursorInfo)[] = [];
if (Text.isText(node) && cursors?.length) {
cursors.forEach((cursor) => {
if (Range.includes(cursor, path)) {
const { focus, anchor, data } = cursor;
const isFocusNode = Path.equals(focus.path, path);
const isAnchorNode = Path.equals(anchor.path, path);
const isForward = Range.isForward({ anchor, focus });
ranges.push({
data,
isForward,
originAnchorPath: anchor.path,
originFocusPath: focus.path,
isCaret: isFocusNode,
anchor: {
path,
// eslint-disable-next-line no-nested-ternary
offset: isAnchorNode
? anchor.offset
: isForward
? 0
: node.text.length,
},
focus: {
path,
// eslint-disable-next-line no-nested-ternary
offset: isFocusNode
? focus.offset
: isForward
? node.text.length
: 0,
},
});
}
});
}
return ranges;
},
[cursors]
);
return { decorate, cursors };
};
export default useCursors;