@textbus/collaborate 2.0.0-alpha.38 → 2.0.0-alpha.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/collab/_api.d.ts +0 -1
- package/bundles/collab/_api.js +0 -1
- package/bundles/collab/_api.js.map +1 -1
- package/bundles/collab/collaborate-cursor.js +51 -16
- package/bundles/collab/collaborate-cursor.js.map +1 -1
- package/bundles/collab/local-to-remote.d.ts +10 -2
- package/bundles/collab/local-to-remote.js +136 -157
- package/bundles/collab/local-to-remote.js.map +1 -1
- package/bundles/collab/remote-to-local.d.ts +10 -1
- package/bundles/collab/remote-to-local.js +132 -117
- package/bundles/collab/remote-to-local.js.map +1 -1
- package/bundles/collaborate.d.ts +22 -6
- package/bundles/collaborate.js +180 -24
- package/bundles/collaborate.js.map +1 -1
- package/bundles/public-api.js.map +1 -1
- package/package.json +4 -4
- package/src/collab/_api.ts +0 -1
- package/src/collab/local-to-remote.ts +133 -159
- package/src/collab/remote-to-local.ts +125 -114
- package/src/collaborate.ts +60 -14
- package/tsconfig-build.json +2 -0
- package/bundles/collab/collaborate-history.d.ts +0 -22
- package/bundles/collab/collaborate-history.js +0 -53
- package/bundles/collab/collaborate-history.js.map +0 -1
- package/src/collab/collaborate-history.ts +0 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@textbus/collaborate",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.39",
|
|
4
4
|
"description": "TextBus is a rich text editor and framework that is highly customizable and extensible to achieve rich wysiwyg effects.",
|
|
5
5
|
"main": "./bundles/public-api.js",
|
|
6
6
|
"module": "./bundles/public-api.js",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@tanbo/di": "^1.0.5",
|
|
29
29
|
"@tanbo/stream": "^0.0.12",
|
|
30
|
-
"@textbus/browser": "^2.0.0-alpha.
|
|
31
|
-
"@textbus/core": "^2.0.0-alpha.
|
|
30
|
+
"@textbus/browser": "^2.0.0-alpha.39",
|
|
31
|
+
"@textbus/core": "^2.0.0-alpha.39",
|
|
32
32
|
"reflect-metadata": "^0.1.13",
|
|
33
33
|
"y-protocols": "^1.0.5",
|
|
34
34
|
"yjs": "^13.5.27"
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"bugs": {
|
|
45
45
|
"url": "https://github.com/textbus/textbus.git/issues"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "a3de1ffbf0b8527eff875cd415b5d8d16df91ecb"
|
|
48
48
|
}
|
package/src/collab/_api.ts
CHANGED
|
@@ -1,184 +1,158 @@
|
|
|
1
|
-
import { Action, Operation, SlotLiteral, ComponentLiteral
|
|
2
|
-
import { Array as YArray, Map as YMap } from 'yjs'
|
|
1
|
+
import { Action, Operation, SlotLiteral, ComponentLiteral } from '@textbus/core'
|
|
2
|
+
import { Array as YArray, Map as YMap, Text as YText } from 'yjs'
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
export class LocalToRemote {
|
|
5
|
+
transform(operation: Operation, root: YText) {
|
|
6
|
+
const path = [...operation.path]
|
|
7
|
+
path.shift()
|
|
8
|
+
if (path.length) {
|
|
9
|
+
const componentIndex = path.shift()!
|
|
10
|
+
const sharedComponent = this.getSharedComponentByIndex(root, componentIndex)
|
|
11
|
+
if (sharedComponent) {
|
|
12
|
+
this.applyComponentOperationToSharedComponent(path, operation.apply, sharedComponent)
|
|
13
|
+
}
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
this.mergeActionsToSharedSlot(root, operation.apply)
|
|
11
17
|
}
|
|
12
|
-
insertContent(root, operation.apply)
|
|
13
|
-
}
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
private applyComponentOperationToSharedComponent(path: number[], actions: Action[], componentYMap: YMap<any>) {
|
|
20
|
+
const sharedSlots = componentYMap.get('slots') as YArray<any>
|
|
21
|
+
if (path.length) {
|
|
22
|
+
const slotIndex = path.shift()!
|
|
23
|
+
const sharedSlot = sharedSlots.get(slotIndex)
|
|
24
|
+
this.applySlotOperationToSharedSlot(path, actions, sharedSlot)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
let index: number
|
|
28
|
+
actions.forEach(action => {
|
|
29
|
+
switch (action.type) {
|
|
30
|
+
case 'retain':
|
|
31
|
+
index = action.offset
|
|
32
|
+
break
|
|
33
|
+
case 'insertSlot':
|
|
34
|
+
sharedSlots.insert(index, [this.makeSharedSlotBySlotLiteral(action.slot)])
|
|
35
|
+
index++
|
|
36
|
+
break
|
|
37
|
+
case 'apply':
|
|
38
|
+
componentYMap.set('state', action.value)
|
|
39
|
+
break
|
|
40
|
+
case 'delete':
|
|
41
|
+
sharedSlots.delete(index, action.count)
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
})
|
|
22
45
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
break
|
|
32
|
-
case 'apply':
|
|
33
|
-
componentYMap.set('state', action.value)
|
|
34
|
-
break
|
|
35
|
-
case 'delete':
|
|
36
|
-
sharedSlots.delete(index, action.count)
|
|
37
|
-
break
|
|
46
|
+
|
|
47
|
+
private applySlotOperationToSharedSlot(path: number[], actions: Action[], slotYMap: YMap<any>) {
|
|
48
|
+
if (path.length) {
|
|
49
|
+
const componentIndex = path.shift()!
|
|
50
|
+
const sharedContent = slotYMap.get('content') as YText
|
|
51
|
+
const sharedComponent = this.getSharedComponentByIndex(sharedContent, componentIndex)!
|
|
52
|
+
this.applyComponentOperationToSharedComponent(path, actions, sharedComponent)
|
|
53
|
+
return
|
|
38
54
|
}
|
|
39
|
-
|
|
40
|
-
}
|
|
55
|
+
const content = slotYMap.get('content') as YText
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
if (path.length) {
|
|
44
|
-
const componentIndex = path.shift()!
|
|
45
|
-
const sharedContent = slotYMap.get('content') as YArray<any>
|
|
46
|
-
const sharedComponent = sharedContent.get(componentIndex)
|
|
47
|
-
applyComponentOperationToSharedComponent(path, actions, sharedComponent)
|
|
48
|
-
return
|
|
57
|
+
this.mergeActionsToSharedSlot(content, actions)
|
|
49
58
|
}
|
|
50
|
-
const content = slotYMap.get('content') as YArray<any>
|
|
51
59
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
private mergeActionsToSharedSlot(content: YText, actions: Action[]) {
|
|
61
|
+
let index: number
|
|
62
|
+
let length: number
|
|
63
|
+
|
|
64
|
+
actions.forEach(action => {
|
|
65
|
+
if (action.type === 'retain') {
|
|
57
66
|
if (action.formats) {
|
|
58
|
-
|
|
67
|
+
content.format(index, action.offset, action.formats)
|
|
68
|
+
} else {
|
|
69
|
+
index = action.offset
|
|
59
70
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
} else if (action.type === 'insert') {
|
|
72
|
+
const delta = content.toDelta()
|
|
73
|
+
const isEmpty = delta.length === 1 && delta[0].insert === '\n'
|
|
74
|
+
|
|
63
75
|
if (typeof action.content === 'string') {
|
|
64
|
-
|
|
65
|
-
content.insert(index, action.content
|
|
76
|
+
length = action.content.length
|
|
77
|
+
content.insert(index, action.content)
|
|
66
78
|
} else {
|
|
67
|
-
|
|
68
|
-
content.
|
|
79
|
+
length = 1
|
|
80
|
+
content.insertEmbed(index, this.makeSharedComponentByComponentLiteral(action.content))
|
|
69
81
|
}
|
|
70
82
|
if (action.formats) {
|
|
71
|
-
|
|
83
|
+
content.format(index, length, action.formats)
|
|
72
84
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (content.length === 0) {
|
|
76
|
-
// 当内容为空时,slot 实例内容为 ['\n'],触发删除会导致长度溢出
|
|
77
|
-
return
|
|
85
|
+
if (isEmpty && index === 0) {
|
|
86
|
+
content.delete(content.length - 1, 1)
|
|
78
87
|
}
|
|
88
|
+
index += length
|
|
89
|
+
} else if (action.type === 'delete') {
|
|
90
|
+
const delta = content.toDelta()
|
|
79
91
|
content.delete(index, action.count)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
92
|
+
if (content.length === 0) {
|
|
93
|
+
content.insert(0, '\n', delta[0]?.attributes)
|
|
94
|
+
}
|
|
95
|
+
} else if (action.type === 'apply') {
|
|
96
|
+
content.setAttribute('state', action.value)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
private makeSharedSlotBySlotLiteral(slotLiteral: SlotLiteral): YMap<any> {
|
|
102
|
+
const content = new YText()
|
|
103
|
+
let index = 0
|
|
104
|
+
slotLiteral.content.forEach(i => {
|
|
105
|
+
let size: number
|
|
106
|
+
if (typeof i === 'string') {
|
|
107
|
+
size = i.length
|
|
108
|
+
content.insert(index, i)
|
|
109
|
+
} else {
|
|
110
|
+
size = 1
|
|
111
|
+
content.insertEmbed(index, this.makeSharedComponentByComponentLiteral(i))
|
|
112
|
+
}
|
|
113
|
+
index += size
|
|
114
|
+
})
|
|
115
|
+
const formats = slotLiteral.formats
|
|
116
|
+
Object.keys(formats).forEach(key => {
|
|
117
|
+
const formatRanges = formats[key]
|
|
118
|
+
formatRanges.forEach(formatRange => {
|
|
119
|
+
content.format(formatRange.startIndex, formatRange.endIndex - formatRange.startIndex, {
|
|
120
|
+
[key]: formatRange.value
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
})
|
|
101
124
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
startIndex,
|
|
108
|
-
endIndex,
|
|
109
|
-
value: formats[key]
|
|
110
|
-
}])
|
|
111
|
-
}
|
|
125
|
+
const sharedSlot = new YMap()
|
|
126
|
+
sharedSlot.set('content', content)
|
|
127
|
+
sharedSlot.set('schema', slotLiteral.schema)
|
|
128
|
+
return sharedSlot
|
|
129
|
+
}
|
|
112
130
|
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
value: formats[key]
|
|
131
|
+
private makeSharedComponentByComponentLiteral(componentLiteral: ComponentLiteral): YMap<any> {
|
|
132
|
+
const slots = new YArray()
|
|
133
|
+
componentLiteral.slots.forEach(item => {
|
|
134
|
+
slots.push([this.makeSharedSlotBySlotLiteral(item)])
|
|
118
135
|
})
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
const sharedComponent = new YMap()
|
|
137
|
+
sharedComponent.set('name', componentLiteral.name)
|
|
138
|
+
sharedComponent.set('slots', slots)
|
|
139
|
+
sharedComponent.set('state', componentLiteral.state)
|
|
140
|
+
return sharedComponent
|
|
141
|
+
}
|
|
122
142
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
case 'insert':
|
|
131
|
-
content.insert(index!, [
|
|
132
|
-
typeof action.content === 'string' ?
|
|
133
|
-
action.content :
|
|
134
|
-
makeSharedComponentByComponentLiteral(action.content)
|
|
135
|
-
])
|
|
136
|
-
if (action.formats) {
|
|
137
|
-
// TODO 根节点样式
|
|
143
|
+
private getSharedComponentByIndex(host: YText, index: number): YMap<any> | null {
|
|
144
|
+
const delta = host.toDelta()
|
|
145
|
+
let i = 0
|
|
146
|
+
for (const action of delta) {
|
|
147
|
+
if (action.insert) {
|
|
148
|
+
if (i === index) {
|
|
149
|
+
return action.insert instanceof YMap ? action.insert : null
|
|
138
150
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function makeSharedSlotBySlotLiteral(slotLiteral: SlotLiteral): YMap<any> {
|
|
148
|
-
const content = new YArray()
|
|
149
|
-
let index = 0
|
|
150
|
-
slotLiteral.content.forEach(i => {
|
|
151
|
-
let size: number
|
|
152
|
-
if (typeof i === 'string') {
|
|
153
|
-
size = i.length
|
|
154
|
-
content.insert(index, [i])
|
|
155
|
-
} else {
|
|
156
|
-
size = 1
|
|
157
|
-
content.insert(index, [makeSharedComponentByComponentLiteral(i)])
|
|
151
|
+
i += action.insert instanceof YMap ? 1 : action.insert.length
|
|
152
|
+
} else {
|
|
153
|
+
throw new Error('xxx')
|
|
154
|
+
}
|
|
158
155
|
}
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
const formats = new YMap()
|
|
162
|
-
Object.keys(slotLiteral.formats).forEach(key => {
|
|
163
|
-
formats.set(key, slotLiteral.formats[key])
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
const sharedSlot = new YMap()
|
|
167
|
-
sharedSlot.set('state', slotLiteral.state)
|
|
168
|
-
sharedSlot.set('content', content)
|
|
169
|
-
sharedSlot.set('schema', slotLiteral.schema)
|
|
170
|
-
sharedSlot.set('formats', formats)
|
|
171
|
-
return sharedSlot
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function makeSharedComponentByComponentLiteral(componentLiteral: ComponentLiteral): YMap<any> {
|
|
175
|
-
const slots = new YArray()
|
|
176
|
-
componentLiteral.slots.forEach(item => {
|
|
177
|
-
slots.push([makeSharedSlotBySlotLiteral(item)])
|
|
178
|
-
})
|
|
179
|
-
const sharedComponent = new YMap()
|
|
180
|
-
sharedComponent.set('name', componentLiteral.name)
|
|
181
|
-
sharedComponent.set('slots', slots)
|
|
182
|
-
sharedComponent.set('state', componentLiteral.state)
|
|
183
|
-
return sharedComponent
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
184
158
|
}
|
|
@@ -1,140 +1,151 @@
|
|
|
1
|
-
import { Map as YMap, YArrayEvent, YEvent, YMapEvent } from 'yjs'
|
|
2
|
-
import { ComponentInstance,
|
|
1
|
+
import { Map as YMap, YArrayEvent, YEvent, YMapEvent, Text as YText, YTextEvent, Array as YArray } from 'yjs'
|
|
2
|
+
import { ComponentInstance, ComponentLiteral, Registry, Slot, Translator } from '@textbus/core'
|
|
3
3
|
|
|
4
4
|
type YPath = [number, string][]
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < ev.path.length; i += 2) {
|
|
11
|
-
path.push(ev.path.slice(i, i + 2) as [number, string])
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (path.length) {
|
|
15
|
-
const componentIndex = path.shift()![0] as number
|
|
16
|
-
const component = slot.getContentAtIndex(componentIndex) as ComponentInstance
|
|
17
|
-
applySharedComponentToComponent(ev, path, component, translator, registry)
|
|
18
|
-
return
|
|
19
|
-
}
|
|
6
|
+
export class RemoteToLocal {
|
|
7
|
+
constructor(private translator: Translator,
|
|
8
|
+
private registry: Registry) {
|
|
9
|
+
}
|
|
20
10
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
transform(events: YEvent[], slot: Slot) {
|
|
12
|
+
events.forEach(ev => {
|
|
13
|
+
const path: YPath = []
|
|
24
14
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const childPath = path.shift()!
|
|
28
|
-
const slot = component.slots.get(childPath[0])!
|
|
29
|
-
applySharedSlotToSlot(ev, path, slot, translator, registry, childPath[1] === 'formats')
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
if (ev instanceof YMapEvent) {
|
|
33
|
-
ev.keysChanged.forEach(key => {
|
|
34
|
-
if (key === 'state') {
|
|
35
|
-
const state = (ev.target as YMap<any>).get('state')
|
|
36
|
-
component.updateState(draft => {
|
|
37
|
-
Object.assign(draft, state)
|
|
38
|
-
})
|
|
15
|
+
for (let i = 0; i < ev.path.length; i += 2) {
|
|
16
|
+
path.push(ev.path.slice(i, i + 2) as [number, string])
|
|
39
17
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
} else if (action.insert) {
|
|
47
|
-
(action.insert as Array<any>).forEach(item => {
|
|
48
|
-
slots.insert(translator.createSlot(item.toJSON())!)
|
|
49
|
-
})
|
|
50
|
-
} else if (action.delete) {
|
|
51
|
-
slots.retain(slots.index)
|
|
52
|
-
slots.delete(action.delete)
|
|
18
|
+
|
|
19
|
+
if (path.length) {
|
|
20
|
+
const componentIndex = path.shift()![0] as number
|
|
21
|
+
const component = slot.getContentAtIndex(componentIndex) as ComponentInstance
|
|
22
|
+
this.applySharedComponentToComponent(ev, path, component)
|
|
23
|
+
return
|
|
53
24
|
}
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
25
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const componentIndex = path.shift()![0]
|
|
61
|
-
const component = slot.getContentAtIndex(componentIndex) as ComponentInstance
|
|
62
|
-
applySharedComponentToComponent(ev, path, component, translator, registry)
|
|
63
|
-
return
|
|
26
|
+
this.applySharedSlotToSlot(ev, path, slot)
|
|
27
|
+
})
|
|
64
28
|
}
|
|
65
29
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} else {
|
|
75
|
-
slot.insert(translator.createComponent(item.toJSON())!)
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
} else if (action.delete) {
|
|
79
|
-
slot.retain(slot.index)
|
|
80
|
-
slot.delete(action.delete)
|
|
81
|
-
}
|
|
82
|
-
})
|
|
83
|
-
} else if (ev instanceof YMapEvent) {
|
|
84
|
-
if (isUpdateFormats) {
|
|
85
|
-
const json = ev.target.toJSON()
|
|
86
|
-
ev.keysChanged.forEach(key => {
|
|
87
|
-
const formats = json[key]
|
|
88
|
-
const formatter = registry.getFormatter(key)!
|
|
89
|
-
if (formatter.type !== FormatType.Block) {
|
|
90
|
-
slot.applyFormat(formatter, {
|
|
91
|
-
startIndex: 0,
|
|
92
|
-
endIndex: slot.length,
|
|
93
|
-
value: null
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
formats.forEach(item => {
|
|
97
|
-
if (formatter.type === FormatType.Block) {
|
|
98
|
-
slot.applyFormat(formatter, item.value)
|
|
99
|
-
} else {
|
|
100
|
-
slot.applyFormat(formatter, item)
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
} else {
|
|
30
|
+
private applySharedComponentToComponent(ev: YEvent, path: YPath, component: ComponentInstance) {
|
|
31
|
+
if (path.length) {
|
|
32
|
+
const childPath = path.shift()!
|
|
33
|
+
const slot = component.slots.get(childPath[0])!
|
|
34
|
+
this.applySharedSlotToSlot(ev, path, slot)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
if (ev instanceof YMapEvent) {
|
|
105
38
|
ev.keysChanged.forEach(key => {
|
|
106
39
|
if (key === 'state') {
|
|
107
40
|
const state = (ev.target as YMap<any>).get('state')
|
|
108
|
-
|
|
41
|
+
component.updateState(draft => {
|
|
109
42
|
Object.assign(draft, state)
|
|
110
43
|
})
|
|
111
44
|
}
|
|
112
45
|
})
|
|
46
|
+
} else if (ev instanceof YArrayEvent) {
|
|
47
|
+
const slots = component.slots
|
|
48
|
+
ev.delta.forEach(action => {
|
|
49
|
+
if (Reflect.has(action, 'retain')) {
|
|
50
|
+
slots.retain(action.retain!)
|
|
51
|
+
} else if (action.insert) {
|
|
52
|
+
(action.insert as Array<any>).forEach(item => {
|
|
53
|
+
slots.insert(this.translator.createSlot(item.toJSON())!)
|
|
54
|
+
})
|
|
55
|
+
} else if (action.delete) {
|
|
56
|
+
slots.retain(slots.index)
|
|
57
|
+
slots.delete(action.delete)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
113
60
|
}
|
|
114
61
|
}
|
|
115
|
-
}
|
|
116
62
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
63
|
+
private applySharedSlotToSlot(ev: YEvent, path: YPath, slot: Slot) {
|
|
64
|
+
if (path.length) {
|
|
65
|
+
path.shift()
|
|
66
|
+
const delta = (ev.target.parent as YText).toDelta()
|
|
67
|
+
let componentIndex = 0
|
|
68
|
+
for (let i = 0; i < delta.length; i++) {
|
|
69
|
+
const action = delta[i]
|
|
70
|
+
if (action.insert === ev.target) {
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
componentIndex += typeof action.insert === 'string' ? action.insert.length : 1
|
|
74
|
+
}
|
|
75
|
+
const component = slot.getContentAtIndex(componentIndex) as ComponentInstance
|
|
76
|
+
this.applySharedComponentToComponent(ev, path, component)
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (ev instanceof YTextEvent) {
|
|
81
|
+
slot.retain(0)
|
|
82
|
+
ev.delta.forEach(action => {
|
|
83
|
+
if (Reflect.has(action, 'retain')) {
|
|
84
|
+
if (action.attributes) {
|
|
85
|
+
slot.retain(action.retain!, Object.keys(action.attributes).map(key => {
|
|
86
|
+
return [this.registry.getFormatter(key)!, action.attributes![key]]
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
slot.retain(action.retain!)
|
|
90
|
+
} else if (action.insert) {
|
|
91
|
+
if (typeof action.insert === 'string') {
|
|
92
|
+
slot.insert(action.insert, action.attributes ? Object.keys(action.attributes).map(key => {
|
|
93
|
+
return [this.registry.getFormatter(key)!, action.attributes![key]]
|
|
94
|
+
}) : [])
|
|
126
95
|
} else {
|
|
127
|
-
const
|
|
128
|
-
const component = translator.createComponent(json)!
|
|
96
|
+
const component = this.createComponentBySharedComponent(action.insert as YMap<any>)
|
|
129
97
|
slot.insert(component)
|
|
130
98
|
}
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
99
|
+
} else if (action.delete) {
|
|
100
|
+
slot.retain(slot.index)
|
|
101
|
+
slot.delete(action.delete)
|
|
102
|
+
} else if (action.attributes) {
|
|
103
|
+
slot.updateState(draft => {
|
|
104
|
+
Object.assign(draft, action.attributes)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
} else {
|
|
109
|
+
throw new Error('xxx')
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private createComponentBySharedComponent(yMap: YMap<any>): ComponentInstance {
|
|
114
|
+
const slots = yMap.get('slots') as YArray<YMap<any>>
|
|
115
|
+
const componentLiteral: ComponentLiteral = {
|
|
116
|
+
state: yMap.get('state'),
|
|
117
|
+
name: yMap.get('name'),
|
|
118
|
+
slots: slots.map(sharedSlot => {
|
|
119
|
+
return this.createSlotBySharedSlot(sharedSlot).toJSON()
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
return this.translator.createComponent(componentLiteral)!
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private createSlotBySharedSlot(sharedSlot: YMap<any>): Slot {
|
|
126
|
+
const content = sharedSlot.get('content') as YText
|
|
127
|
+
const delta = content.toDelta()
|
|
128
|
+
|
|
129
|
+
const slot = this.translator.createSlot({
|
|
130
|
+
schema: sharedSlot.get('schema'),
|
|
131
|
+
state: sharedSlot.get('state'),
|
|
132
|
+
formats: {},
|
|
133
|
+
content: []
|
|
138
134
|
})
|
|
135
|
+
|
|
136
|
+
for (const action of delta) {
|
|
137
|
+
if (action.insert) {
|
|
138
|
+
if (typeof action.insert === 'string') {
|
|
139
|
+
slot.insert(action.insert, action.attributes ? Object.keys(action.attributes).map(key => {
|
|
140
|
+
return [this.registry.getFormatter(key)!, action.attributes![key]]
|
|
141
|
+
}) : [])
|
|
142
|
+
} else {
|
|
143
|
+
slot.insert(this.createComponentBySharedComponent(action.insert))
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
throw new Error('xxx')
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return slot
|
|
139
150
|
}
|
|
140
151
|
}
|