You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
69 lines
1.7 KiB
69 lines
1.7 KiB
import { useState, useCallback, useEffect, useMemo } from 'react'
|
|
|
|
import { Text, Range, Path, NodeEntry } from 'slate'
|
|
|
|
import { toJS, Cursor, Cursors } from '@slate-collaborative/bridge'
|
|
|
|
import { AutomergeEditor } from './automerge-editor'
|
|
|
|
const useCursor = (
|
|
e: AutomergeEditor
|
|
): { decorate: (entry: NodeEntry) => Range[]; cursors: Cursor[] } => {
|
|
const [cursorData, setSursorData] = useState<Cursor[]>([])
|
|
|
|
useEffect(() => {
|
|
e.onCursor = (data: Cursors) => {
|
|
const ranges: Cursor[] = []
|
|
|
|
const cursors = toJS(data)
|
|
|
|
for (let cursor in cursors) {
|
|
if (cursor !== e.clientId && cursors[cursor]) {
|
|
ranges.push(JSON.parse(cursors[cursor]))
|
|
}
|
|
}
|
|
|
|
setSursorData(ranges)
|
|
}
|
|
}, [])
|
|
|
|
const cursors = useMemo<Cursor[]>(() => cursorData, [cursorData])
|
|
|
|
const decorate = useCallback(
|
|
([node, path]: NodeEntry) => {
|
|
const ranges: Range[] = []
|
|
|
|
if (Text.isText(node) && cursors?.length) {
|
|
cursors.forEach(cursor => {
|
|
if (Range.includes(cursor, path)) {
|
|
const { focus, anchor, isForward } = cursor
|
|
|
|
ranges.push({
|
|
...cursor,
|
|
isCaret: isForward
|
|
? Path.equals(focus.path, path)
|
|
: Path.equals(anchor.path, path),
|
|
anchor: Path.isBefore(anchor.path, path)
|
|
? { ...anchor, offset: 0 }
|
|
: anchor,
|
|
focus: Path.isAfter(focus.path, path)
|
|
? { ...focus, offset: node.text.length }
|
|
: focus
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
return ranges
|
|
},
|
|
[cursors]
|
|
)
|
|
|
|
return {
|
|
cursors,
|
|
decorate
|
|
}
|
|
}
|
|
|
|
export default useCursor
|