mobx-keystone-yjs
Version:
Yjs bindings for mobx-keystone
93 lines (81 loc) • 2.29 kB
text/typescript
import { Patch } from "mobx-keystone"
import * as Y from "yjs"
import { PlainArray, PlainObject, PlainValue } from "../plainTypes"
import { failure } from "../utils/error"
export function convertYjsEventToPatches(event: Y.YEvent<any>): Patch[] {
const patches: Patch[] = []
if (event instanceof Y.YMapEvent) {
const source = event.target
event.changes.keys.forEach((change, key) => {
const path = [...event.path, key]
switch (change.action) {
case "add":
patches.push({
op: "add",
path,
value: toPlainValue(source.get(key)),
})
break
case "update":
patches.push({
op: "replace",
path,
value: toPlainValue(source.get(key)),
})
break
case "delete":
patches.push({
op: "remove",
path,
})
break
default:
throw failure(`unsupported Yjs map event action: ${change.action}`)
}
})
} else if (event instanceof Y.YArrayEvent) {
let retain = 0
event.changes.delta.forEach((change) => {
if (change.retain) {
retain += change.retain
}
if (change.delete) {
// remove X items at retain position
const path = [...event.path, retain]
for (let i = 0; i < change.delete; i++) {
patches.push({
op: "remove",
path,
})
}
}
if (change.insert) {
const newValues = Array.isArray(change.insert) ? change.insert : [change.insert]
newValues.forEach((v) => {
const path = [...event.path, retain]
patches.push({
op: "add",
path,
value: toPlainValue(v),
})
retain++
})
}
})
} else if (event instanceof Y.YTextEvent) {
const path = [...event.path, "deltaList", -1 /* last item */]
patches.push({
op: "add",
path,
value: { $frozen: true, data: event.delta },
})
}
return patches
}
function toPlainValue(v: Y.Map<any> | Y.Array<any> | PlainValue) {
if (v instanceof Y.Map || v instanceof Y.Array) {
return v.toJSON() as PlainObject | PlainArray
} else {
return v
}
}