mirror of
https://github.com/cudr/slate-collaborative.git
synced 2024-10-27 20:34:06 +00:00
101 lines
2.4 KiB
TypeScript
101 lines
2.4 KiB
TypeScript
import { useState, useCallback, useEffect, useMemo } from 'react'
|
|
|
|
import { Text, Range, Path, NodeEntry } from 'slate'
|
|
|
|
import { toJS, Cursor, Cursors } from '../bridge/index'
|
|
|
|
import useMounted from './useMounted'
|
|
import { AutomergeEditor } from './interfaces'
|
|
|
|
const useCursor = (
|
|
e: AutomergeEditor
|
|
): { decorate: (entry: NodeEntry) => Range[]; cursors: Cursor[] } => {
|
|
const [cursorData, setCursorData] = useState<Cursor[]>([])
|
|
const mountedRef = useMounted()
|
|
|
|
useEffect(() => {
|
|
e.onCursor = (data: Cursors) => {
|
|
if (!mountedRef.current) return
|
|
|
|
const ranges: Cursor[] = []
|
|
|
|
// If the cursor data is null or undefined, unset all active cursors
|
|
if (!data) {
|
|
setCursorData(ranges)
|
|
return
|
|
}
|
|
|
|
try {
|
|
const cursors = toJS(data)
|
|
|
|
for (let cursor in cursors) {
|
|
if (cursor !== e.clientId && cursors[cursor]) {
|
|
ranges.push(JSON.parse(cursors[cursor]))
|
|
}
|
|
}
|
|
|
|
// only update state if this component is still mounted
|
|
if (mountedRef.current) {
|
|
setCursorData(ranges)
|
|
}
|
|
} catch (err) {
|
|
e.handleError(err, {
|
|
type: 'onCursor',
|
|
data,
|
|
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
|
|
|
|
const isFocusNode = Path.equals(focus.path, path)
|
|
const isAnchorNode = Path.equals(anchor.path, path)
|
|
|
|
ranges.push({
|
|
...cursor,
|
|
isCaret: isFocusNode,
|
|
anchor: {
|
|
path,
|
|
offset: isAnchorNode
|
|
? anchor.offset
|
|
: isForward
|
|
? 0
|
|
: node.text.length
|
|
},
|
|
focus: {
|
|
path,
|
|
offset: isFocusNode
|
|
? focus.offset
|
|
: isForward
|
|
? node.text.length
|
|
: 0
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
return ranges
|
|
},
|
|
[cursors]
|
|
)
|
|
|
|
return {
|
|
cursors,
|
|
decorate
|
|
}
|
|
}
|
|
|
|
export default useCursor
|