feat: add test annotations

pull/2/head
cudr 5 years ago
parent ab3b8c4ee3
commit 2a4d64b074

@ -32,6 +32,8 @@ class Connection {
private appendDoc = (path: string, value: ValueJSON) => { private appendDoc = (path: string, value: ValueJSON) => {
const sync = toSync(value) const sync = toSync(value)
sync.cursors = {}
const doc = Automerge.from(sync) const doc = Automerge.from(sync)
this.docSet.setDoc(path, doc) this.docSet.setDoc(path, doc)

@ -1,14 +1,17 @@
import { Operation, SyncDoc } from '../model' import { Operation, SyncDoc } from '../model'
export const addAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { export const addAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => {
console.log('addAnnotation!!!', op)
return doc return doc
} }
export const removeAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { export const removeAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => {
console.log('removeAnnotation!!!', op)
return doc return doc
} }
export const setAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { export const setAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => {
console.log('setAnnotation!!!', op)
return doc return doc
} }

@ -5,7 +5,31 @@ import mark from './mark'
import text from './text' import text from './text'
import annotation from './annotation' import annotation from './annotation'
const setSelection = doc => doc const setSelection = (doc, op, { id, selection }) => {
console.log('selection', selection.toJSON())
if (!doc.cursors) {
doc.cursors = {}
}
// console.log('setSelection', op.toJSON(), id)
const operation = op.toJSON()
if (!doc.cursors[id]) {
doc.cursors[id] = {
key: id,
type: 'collaborative_selection'
}
}
const cursor = doc.cursors[id]
const { focus, anchor } = operation.newProperties
if (focus) cursor.focus = focus
if (anchor) cursor.anchor = anchor
return doc
}
const setValue = (doc, op) => doc const setValue = (doc, op) => doc
const opType: any = { const opType: any = {
@ -18,13 +42,16 @@ const opType: any = {
set_value: setValue set_value: setValue
} }
export const applyOperation = (doc: SyncDoc, op: Operation): SyncDoc => { export const applyOperation = meta => (
doc: SyncDoc,
op: Operation
): SyncDoc => {
try { try {
const applyOp = opType[op.type] const applyOp = opType[op.type]
if (!applyOp) throw new TypeError('Invalid operation type!') if (!applyOp) throw new TypeError('Invalid operation type!')
return applyOp(doc, op) return applyOp(doc, op, meta)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
@ -32,5 +59,5 @@ export const applyOperation = (doc: SyncDoc, op: Operation): SyncDoc => {
} }
} }
export const applySlateOps = (doc: SyncDoc, operations: Operations) => export const applySlateOps = (doc: SyncDoc, operations: Operations, meta) =>
operations.reduce(applyOperation, doc) operations.reduce(applyOperation(meta), doc)

@ -15,18 +15,20 @@ const insertNodeOp = ({ value, obj, index, path }: Automerge.Diff) => map => {
const inserate = ({ nodes, ...json }: any, path) => { const inserate = ({ nodes, ...json }: any, path) => {
const node = nodes ? { ...json, nodes: [] } : json const node = nodes ? { ...json, nodes: [] } : json
if (node.object === 'mark') { if (node.object) {
ops.push({ if (node.object === 'mark') {
type: 'add_mark', ops.push({
path: path.slice(0, -1), type: 'add_mark',
mark: node path: path.slice(0, -1),
}) mark: node
} else { })
ops.push({ } else {
type: 'insert_node', ops.push({
path, type: 'insert_node',
node path,
}) node
})
}
} }
nodes && nodes.forEach((n, i) => inserate(n, [...path, i])) nodes && nodes.forEach((n, i) => inserate(n, [...path, i]))

@ -21,7 +21,7 @@ const opSet = (op: Automerge.Diff, [map, ops]) => {
if (set && path) { if (set && path) {
ops.push(set(op)) ops.push(set(op))
} else { } else if (map[obj]) {
map[obj][key] = link ? map[value] : value map[obj][key] = link ? map[value] : value
} }

@ -1,7 +1,14 @@
import toSync from './toSync' import toSync from './toSync'
import hexGen from './hexGen' import hexGen from './hexGen'
export const toJS = node => JSON.parse(JSON.stringify(node)) export const toJS = node => {
try {
return JSON.parse(JSON.stringify(node))
} catch (e) {
console.error(e)
return null
}
}
export const cloneNode = node => toSync(toJS(node)) export const cloneNode = node => toSync(toJS(node))

@ -5,7 +5,12 @@ import io from 'socket.io-client'
import { Value, Operation } from 'slate' import { Value, Operation } from 'slate'
import { ConnectionModel } from './model' import { ConnectionModel } from './model'
import { applySlateOps, toSlateOp, toJS } from '@slate-collaborative/bridge' import {
applySlateOps,
toSlateOp,
hexGen,
toJS
} from '@slate-collaborative/bridge'
class Connection { class Connection {
url: string url: string
@ -15,6 +20,7 @@ class Connection {
socket: SocketIOClient.Socket socket: SocketIOClient.Socket
editor: any editor: any
connectOpts: any connectOpts: any
selection: any
onConnect?: () => void onConnect?: () => void
onDisconnect?: () => void onDisconnect?: () => void
@ -68,13 +74,47 @@ class Connection {
}) })
}) })
setTimeout(() => (this.editor.remote = false), 5) await Promise.resolve()
this.editor.remote = false
this.setCursors(docNew.cursors)
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
} }
setCursors = cursors => {
if (!cursors) return
// console.log('setCursors', cursors)
const {
value: { annotations }
} = this.editor
const keyMap = {}
this.editor.withoutSaving(() => {
annotations.forEach(anno => {
// if (cursors[anno.key]) {
// console.log('set cursor', anno, )
// this.editor.setAnnotation(anno, cursors[anno.key])
// keyMap[anno.key] = true
// } else {
// this.editor.removeAnnotation(anno)
// }
this.editor.removeAnnotation(anno)
})
Object.keys(cursors).forEach(key => {
if (key !== this.socket.id && !keyMap[key]) {
this.editor.addAnnotation(cursors[key])
}
})
})
}
receiveSlateOps = (operations: Immutable.List<Operation>) => { receiveSlateOps = (operations: Immutable.List<Operation>) => {
const doc = this.docSet.getDoc(this.docId) const doc = this.docSet.getDoc(this.docId)
const message = `change from ${this.socket.id}` const message = `change from ${this.socket.id}`
@ -82,7 +122,10 @@ class Connection {
if (!doc) return if (!doc) return
const changed = Automerge.change(doc, message, (d: any) => const changed = Automerge.change(doc, message, (d: any) =>
applySlateOps(d, operations) applySlateOps(d, operations, {
id: this.socket.id,
selection: this.editor.value.selection
})
) )
this.docSet.setDoc(this.docId, changed) this.docSet.setDoc(this.docId, changed)

@ -2,6 +2,7 @@ import { ReactNode } from 'react'
import onChange from './onChange' import onChange from './onChange'
import renderEditor from './renderEditor' import renderEditor from './renderEditor'
import renderAnnotation from './renderAnnotation'
import Connection from './Connection' import Connection from './Connection'
@ -22,7 +23,8 @@ const plugin = (opts: PluginOptions = {}) => {
return { return {
onChange: onChange(options), onChange: onChange(options),
renderEditor: renderEditor(options) renderEditor: renderEditor(options),
renderAnnotation
} }
} }

@ -1,7 +1,7 @@
import { ExtendedEditor } from './model' import { ExtendedEditor } from './model'
const onChange = opts => (editor: ExtendedEditor, next: () => void) => { const onChange = opts => (editor: ExtendedEditor, next: () => void) => {
if (!editor.remote) { if (editor.connection && !editor.remote) {
const operations: any = editor.operations const operations: any = editor.operations
editor.connection.receiveSlateOps(operations) editor.connection.receiveSlateOps(operations)

@ -0,0 +1,43 @@
import React from 'react'
const wrapStyles = { backgroundColor: '#e91e63', position: 'relative' }
const cursorStyleBase = {
position: 'absolute',
top: 0,
pointerEvents: 'none',
userSelect: 'none',
transform: 'translateY(-100%)',
fontSize: '10px',
color: 'white',
background: 'palevioletred',
whiteSpace: 'nowrap'
} as any
const renderAnnotation = (props, editor, next) => {
const { children, annotation, attributes } = props
const isLeft = annotation.focus.offset >= annotation.anchor.offset
console.log('isLeft', isLeft)
const cursorStyles = { ...cursorStyleBase, left: isLeft ? '0%' : '100%' }
console.log('renderAnnotation', annotation.toJSON())
switch (annotation.type) {
case 'collaborative_selection':
return (
<span {...attributes} style={wrapStyles}>
<span contentEditable={false} style={cursorStyles}>
{annotation.key}
</span>
{children}
</span>
)
default:
return next()
}
}
export default renderAnnotation
Loading…
Cancel
Save