@textbus/collaborate 2.0.0-alpha.36 → 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 +3 -0
- package/bundles/collab/_api.js +4 -0
- package/bundles/collab/_api.js.map +1 -0
- package/bundles/collab/collaborate-cursor.d.ts +25 -0
- package/bundles/collab/collaborate-cursor.js +143 -0
- package/bundles/collab/collaborate-cursor.js.map +1 -0
- package/bundles/collab/local-to-remote.d.ts +11 -0
- package/bundles/collab/local-to-remote.js +155 -0
- package/bundles/collab/local-to-remote.js.map +1 -0
- package/bundles/collab/remote-to-local.d.ts +12 -0
- package/bundles/collab/remote-to-local.js +158 -0
- package/bundles/collab/remote-to-local.js.map +1 -0
- package/bundles/collaborate.d.ts +29 -5
- package/bundles/collaborate.js +191 -20
- package/bundles/collaborate.js.map +1 -1
- package/bundles/public-api.d.ts +1 -0
- package/bundles/public-api.js +1 -0
- package/bundles/public-api.js.map +1 -1
- package/package.json +7 -6
- package/src/collab/_api.ts +3 -0
- package/src/collab/collaborate-cursor.ts +107 -0
- package/src/collab/local-to-remote.ts +158 -0
- package/src/collab/remote-to-local.ts +151 -0
- package/src/collaborate.ts +78 -15
- package/src/public-api.ts +1 -0
- package/tsconfig-build.json +2 -0
- package/bundles/local-to-remote.d.ts +0 -3
- package/bundles/local-to-remote.js +0 -173
- package/bundles/local-to-remote.js.map +0 -1
- package/bundles/remote-to-local.d.ts +0 -3
- package/bundles/remote-to-local.js +0 -143
- package/bundles/remote-to-local.js.map +0 -1
- package/src/local-to-remote.ts +0 -180
- package/src/remote-to-local.ts +0 -140
package/bundles/collaborate.js
CHANGED
@@ -8,39 +8,205 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
9
9
|
};
|
10
10
|
import { Injectable } from '@tanbo/di';
|
11
|
-
import { debounceTime, filter, tap } from '@tanbo/stream';
|
12
|
-
import { RootComponentRef, Starter, Translator,
|
13
|
-
import { Doc as YDoc } from 'yjs';
|
14
|
-
import {
|
15
|
-
import {
|
16
|
-
|
11
|
+
import { debounceTime, filter, Subject, tap } from '@tanbo/stream';
|
12
|
+
import { RootComponentRef, Starter, Translator, Registry, Selection, Renderer } from '@textbus/core';
|
13
|
+
import { Doc as YDoc, UndoManager } from 'yjs';
|
14
|
+
import { LocalToRemote } from './collab/local-to-remote';
|
15
|
+
import { RemoteToLocal } from './collab/remote-to-local';
|
16
|
+
import { CollaborateCursor } from './collab/_api';
|
17
17
|
let Collaborate = class Collaborate {
|
18
|
-
constructor(rootComponentRef, translator,
|
19
|
-
this
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
this
|
18
|
+
constructor(rootComponentRef, collaborateCursor, translator, renderer, registry, selection, starter) {
|
19
|
+
Object.defineProperty(this, "rootComponentRef", {
|
20
|
+
enumerable: true,
|
21
|
+
configurable: true,
|
22
|
+
writable: true,
|
23
|
+
value: rootComponentRef
|
24
|
+
});
|
25
|
+
Object.defineProperty(this, "collaborateCursor", {
|
26
|
+
enumerable: true,
|
27
|
+
configurable: true,
|
28
|
+
writable: true,
|
29
|
+
value: collaborateCursor
|
30
|
+
});
|
31
|
+
Object.defineProperty(this, "translator", {
|
32
|
+
enumerable: true,
|
33
|
+
configurable: true,
|
34
|
+
writable: true,
|
35
|
+
value: translator
|
36
|
+
});
|
37
|
+
Object.defineProperty(this, "renderer", {
|
38
|
+
enumerable: true,
|
39
|
+
configurable: true,
|
40
|
+
writable: true,
|
41
|
+
value: renderer
|
42
|
+
});
|
43
|
+
Object.defineProperty(this, "registry", {
|
44
|
+
enumerable: true,
|
45
|
+
configurable: true,
|
46
|
+
writable: true,
|
47
|
+
value: registry
|
48
|
+
});
|
49
|
+
Object.defineProperty(this, "selection", {
|
50
|
+
enumerable: true,
|
51
|
+
configurable: true,
|
52
|
+
writable: true,
|
53
|
+
value: selection
|
54
|
+
});
|
55
|
+
Object.defineProperty(this, "starter", {
|
56
|
+
enumerable: true,
|
57
|
+
configurable: true,
|
58
|
+
writable: true,
|
59
|
+
value: starter
|
60
|
+
});
|
61
|
+
Object.defineProperty(this, "onSelectionChange", {
|
62
|
+
enumerable: true,
|
63
|
+
configurable: true,
|
64
|
+
writable: true,
|
65
|
+
value: void 0
|
66
|
+
});
|
67
|
+
Object.defineProperty(this, "yDoc", {
|
68
|
+
enumerable: true,
|
69
|
+
configurable: true,
|
70
|
+
writable: true,
|
71
|
+
value: new YDoc()
|
72
|
+
});
|
73
|
+
Object.defineProperty(this, "onBack", {
|
74
|
+
enumerable: true,
|
75
|
+
configurable: true,
|
76
|
+
writable: true,
|
77
|
+
value: void 0
|
78
|
+
});
|
79
|
+
Object.defineProperty(this, "onForward", {
|
80
|
+
enumerable: true,
|
81
|
+
configurable: true,
|
82
|
+
writable: true,
|
83
|
+
value: void 0
|
84
|
+
});
|
85
|
+
Object.defineProperty(this, "onChange", {
|
86
|
+
enumerable: true,
|
87
|
+
configurable: true,
|
88
|
+
writable: true,
|
89
|
+
value: void 0
|
90
|
+
});
|
91
|
+
Object.defineProperty(this, "onPush", {
|
92
|
+
enumerable: true,
|
93
|
+
configurable: true,
|
94
|
+
writable: true,
|
95
|
+
value: void 0
|
96
|
+
});
|
97
|
+
Object.defineProperty(this, "localToRemote", {
|
98
|
+
enumerable: true,
|
99
|
+
configurable: true,
|
100
|
+
writable: true,
|
101
|
+
value: new LocalToRemote()
|
102
|
+
});
|
103
|
+
Object.defineProperty(this, "remoteToLocal", {
|
104
|
+
enumerable: true,
|
105
|
+
configurable: true,
|
106
|
+
writable: true,
|
107
|
+
value: new RemoteToLocal(this.translator, this.registry)
|
108
|
+
});
|
109
|
+
Object.defineProperty(this, "backEvent", {
|
110
|
+
enumerable: true,
|
111
|
+
configurable: true,
|
112
|
+
writable: true,
|
113
|
+
value: new Subject()
|
114
|
+
});
|
115
|
+
Object.defineProperty(this, "forwardEvent", {
|
116
|
+
enumerable: true,
|
117
|
+
configurable: true,
|
118
|
+
writable: true,
|
119
|
+
value: new Subject()
|
120
|
+
});
|
121
|
+
Object.defineProperty(this, "changeEvent", {
|
122
|
+
enumerable: true,
|
123
|
+
configurable: true,
|
124
|
+
writable: true,
|
125
|
+
value: new Subject()
|
126
|
+
});
|
127
|
+
Object.defineProperty(this, "pushEvent", {
|
128
|
+
enumerable: true,
|
129
|
+
configurable: true,
|
130
|
+
writable: true,
|
131
|
+
value: new Subject()
|
132
|
+
});
|
133
|
+
Object.defineProperty(this, "manager", {
|
134
|
+
enumerable: true,
|
135
|
+
configurable: true,
|
136
|
+
writable: true,
|
137
|
+
value: void 0
|
138
|
+
});
|
139
|
+
Object.defineProperty(this, "subscriptions", {
|
140
|
+
enumerable: true,
|
141
|
+
configurable: true,
|
142
|
+
writable: true,
|
143
|
+
value: []
|
144
|
+
});
|
145
|
+
Object.defineProperty(this, "updateFromSelf", {
|
146
|
+
enumerable: true,
|
147
|
+
configurable: true,
|
148
|
+
writable: true,
|
149
|
+
value: true
|
150
|
+
});
|
151
|
+
Object.defineProperty(this, "selectionChangeEvent", {
|
152
|
+
enumerable: true,
|
153
|
+
configurable: true,
|
154
|
+
writable: true,
|
155
|
+
value: new Subject()
|
156
|
+
});
|
157
|
+
this.onSelectionChange = this.selectionChangeEvent.asObservable();
|
158
|
+
this.onBack = this.backEvent.asObservable();
|
159
|
+
this.onForward = this.forwardEvent.asObservable();
|
160
|
+
this.onChange = this.changeEvent.asObservable();
|
161
|
+
this.onPush = this.pushEvent.asObservable();
|
162
|
+
}
|
163
|
+
get canBack() {
|
164
|
+
var _a;
|
165
|
+
return (_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo();
|
166
|
+
}
|
167
|
+
get canForward() {
|
168
|
+
var _a;
|
169
|
+
return (_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo();
|
26
170
|
}
|
27
171
|
setup() {
|
28
172
|
this.subscriptions.push(this.starter.onReady.subscribe(() => {
|
29
|
-
this.
|
173
|
+
this.listen2();
|
174
|
+
}), this.selection.onChange.subscribe(() => {
|
175
|
+
const paths = this.selection.getPaths();
|
176
|
+
this.selectionChangeEvent.next(paths);
|
30
177
|
}));
|
31
178
|
}
|
179
|
+
updateRemoteSelection(paths) {
|
180
|
+
this.collaborateCursor.draw(paths);
|
181
|
+
}
|
182
|
+
listen() {
|
183
|
+
//
|
184
|
+
}
|
185
|
+
back() {
|
186
|
+
if (this.canBack) {
|
187
|
+
this.manager.undo();
|
188
|
+
}
|
189
|
+
}
|
190
|
+
forward() {
|
191
|
+
if (this.canForward) {
|
192
|
+
this.manager.redo();
|
193
|
+
}
|
194
|
+
}
|
32
195
|
destroy() {
|
33
196
|
this.subscriptions.forEach(i => i.unsubscribe());
|
34
197
|
}
|
35
|
-
|
36
|
-
const root = this.yDoc.
|
198
|
+
listen2() {
|
199
|
+
const root = this.yDoc.getText('content');
|
37
200
|
const slot = this.rootComponentRef.component.slots.get(0);
|
201
|
+
this.manager = new UndoManager(root);
|
38
202
|
root.observeDeep((events, transaction) => {
|
39
203
|
if (transaction.origin === this.yDoc) {
|
40
204
|
return;
|
41
205
|
}
|
42
206
|
this.updateFromSelf = false;
|
43
|
-
remoteToLocal(events, slot
|
207
|
+
this.remoteToLocal.transform(events, slot);
|
208
|
+
this.renderer.render();
|
209
|
+
this.selection.restore();
|
44
210
|
this.updateFromSelf = true;
|
45
211
|
});
|
46
212
|
const operations = [];
|
@@ -51,18 +217,23 @@ let Collaborate = class Collaborate {
|
|
51
217
|
}), debounceTime(1)).subscribe(() => {
|
52
218
|
this.yDoc.transact(() => {
|
53
219
|
operations.forEach(operation => {
|
54
|
-
localToRemote(operation, root);
|
220
|
+
this.localToRemote.transform(operation, root);
|
55
221
|
});
|
56
222
|
operations.length = 0;
|
57
223
|
}, this.yDoc);
|
224
|
+
this.renderer.render();
|
225
|
+
this.selection.restore();
|
58
226
|
}));
|
59
227
|
}
|
60
228
|
};
|
61
229
|
Collaborate = __decorate([
|
62
230
|
Injectable(),
|
63
231
|
__metadata("design:paramtypes", [RootComponentRef,
|
232
|
+
CollaborateCursor,
|
64
233
|
Translator,
|
65
|
-
|
234
|
+
Renderer,
|
235
|
+
Registry,
|
236
|
+
Selection,
|
66
237
|
Starter])
|
67
238
|
], Collaborate);
|
68
239
|
export { Collaborate };
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"collaborate.js","sourceRoot":"","sources":["../src/collaborate.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAgB,GAAG,EAAE,MAAM,eAAe,CAAA;
|
1
|
+
{"version":3,"file":"collaborate.js","sourceRoot":"","sources":["../src/collaborate.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@tanbo/di'\nimport { debounceTime, filter, Observable, Subject, Subscription, tap } from '@tanbo/stream'\nimport {\n RootComponentRef,\n Starter,\n Operation,\n Translator,\n Registry,\n Selection,\n SelectionPaths,\n History, Renderer\n} from '@textbus/core'\nimport { Doc as YDoc, UndoManager } from 'yjs'\nimport { LocalToRemote } from './collab/local-to-remote'\nimport { RemoteToLocal } from './collab/remote-to-local'\nimport { CollaborateCursor, RemoteSelection } from './collab/_api'\n\n@Injectable()\nexport class Collaborate implements History {\n onSelectionChange: Observable<SelectionPaths>\n yDoc = new YDoc()\n onBack: Observable<void>\n onForward: Observable<void>\n onChange: Observable<any>\n onPush: Observable<void>\n\n get canBack() {\n return this.manager?.canUndo()\n }\n\n get canForward() {\n return this.manager?.canRedo()\n }\n\n private localToRemote = new LocalToRemote()\n private remoteToLocal = new RemoteToLocal(this.translator, this.registry)\n\n private backEvent = new Subject<void>()\n private forwardEvent = new Subject<void>()\n private changeEvent = new Subject<void>()\n private pushEvent = new Subject<void>()\n\n private manager!: UndoManager\n\n private subscriptions: Subscription[] = []\n private updateFromSelf = true\n\n private selectionChangeEvent = new Subject<SelectionPaths>()\n\n constructor(private rootComponentRef: RootComponentRef,\n private collaborateCursor: CollaborateCursor,\n private translator: Translator,\n private renderer: Renderer,\n private registry: Registry,\n private selection: Selection,\n private starter: Starter) {\n this.onSelectionChange = this.selectionChangeEvent.asObservable()\n this.onBack = this.backEvent.asObservable()\n this.onForward = this.forwardEvent.asObservable()\n this.onChange = this.changeEvent.asObservable()\n this.onPush = this.pushEvent.asObservable()\n }\n\n setup() {\n this.subscriptions.push(\n this.starter.onReady.subscribe(() => {\n this.listen2()\n }),\n this.selection.onChange.subscribe(() => {\n const paths = this.selection.getPaths()\n this.selectionChangeEvent.next(paths)\n })\n )\n }\n\n updateRemoteSelection(paths: RemoteSelection[]) {\n this.collaborateCursor.draw(paths)\n }\n\n listen() {\n //\n }\n\n back() {\n if (this.canBack) {\n this.manager.undo()\n }\n }\n\n forward() {\n if (this.canForward) {\n this.manager.redo()\n }\n }\n\n destroy() {\n this.subscriptions.forEach(i => i.unsubscribe())\n }\n\n private listen2() {\n const root = this.yDoc.getText('content')\n const slot = this.rootComponentRef.component.slots.get(0)!\n this.manager = new UndoManager(root)\n root.observeDeep((events, transaction) => {\n if (transaction.origin === this.yDoc) {\n return\n }\n this.updateFromSelf = false\n\n this.remoteToLocal.transform(events, slot)\n this.renderer.render()\n this.selection.restore()\n this.updateFromSelf = true\n })\n const operations: Operation[] = []\n this.subscriptions.push(\n this.rootComponentRef.component.changeMarker.onChange.pipe(\n filter(() => {\n return this.updateFromSelf\n }),\n tap(op => {\n operations.push(op)\n }),\n debounceTime(1)\n ).subscribe(() => {\n this.yDoc.transact(() => {\n operations.forEach(operation => {\n this.localToRemote.transform(operation, root)\n })\n operations.length = 0\n }, this.yDoc)\n this.renderer.render()\n this.selection.restore()\n })\n )\n }\n}\n"]}
|
package/bundles/public-api.d.ts
CHANGED
package/bundles/public-api.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA"}
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA","sourcesContent":["export * from './collab/_api'\nexport * from './collaborate'\n"]}
|
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",
|
@@ -26,11 +26,12 @@
|
|
26
26
|
],
|
27
27
|
"dependencies": {
|
28
28
|
"@tanbo/di": "^1.0.5",
|
29
|
-
"@tanbo/stream": "^0.0.
|
30
|
-
"@textbus/
|
29
|
+
"@tanbo/stream": "^0.0.12",
|
30
|
+
"@textbus/browser": "^2.0.0-alpha.39",
|
31
|
+
"@textbus/core": "^2.0.0-alpha.39",
|
31
32
|
"reflect-metadata": "^0.1.13",
|
32
|
-
"
|
33
|
-
"
|
33
|
+
"y-protocols": "^1.0.5",
|
34
|
+
"yjs": "^13.5.27"
|
34
35
|
},
|
35
36
|
"author": {
|
36
37
|
"name": "Tanbo",
|
@@ -43,5 +44,5 @@
|
|
43
44
|
"bugs": {
|
44
45
|
"url": "https://github.com/textbus/textbus.git/issues"
|
45
46
|
},
|
46
|
-
"gitHead": "
|
47
|
+
"gitHead": "a3de1ffbf0b8527eff875cd415b5d8d16df91ecb"
|
47
48
|
}
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import { Inject, Injectable } from '@tanbo/di'
|
2
|
+
import { createElement, EDITABLE_DOCUMENT, EDITOR_CONTAINER, SelectionBridge } from '@textbus/browser'
|
3
|
+
import { Selection, SelectionPaths } from '@textbus/core'
|
4
|
+
import { Subject } from '@tanbo/stream'
|
5
|
+
|
6
|
+
export interface RemoteSelection {
|
7
|
+
color: string
|
8
|
+
username: string
|
9
|
+
paths: SelectionPaths
|
10
|
+
}
|
11
|
+
|
12
|
+
export interface SelectionRect {
|
13
|
+
x: number
|
14
|
+
y: number
|
15
|
+
width: number
|
16
|
+
height: number
|
17
|
+
color: string
|
18
|
+
}
|
19
|
+
|
20
|
+
@Injectable()
|
21
|
+
export class CollaborateCursor {
|
22
|
+
private canvas = createElement('canvas', {
|
23
|
+
styles: {
|
24
|
+
position: 'absolute',
|
25
|
+
opacity: 0.5,
|
26
|
+
left: 0,
|
27
|
+
top: 0,
|
28
|
+
width: '100%',
|
29
|
+
height: '100%',
|
30
|
+
pointerEvents: 'none'
|
31
|
+
}
|
32
|
+
}) as HTMLCanvasElement
|
33
|
+
private context = this.canvas.getContext('2d')!
|
34
|
+
|
35
|
+
private onRectChange = new Subject<SelectionRect>()
|
36
|
+
|
37
|
+
constructor(@Inject(EDITOR_CONTAINER) private container: HTMLElement,
|
38
|
+
@Inject(EDITABLE_DOCUMENT) private document: Document,
|
39
|
+
private nativeSelection: SelectionBridge,
|
40
|
+
private selection: Selection) {
|
41
|
+
container.appendChild(this.canvas)
|
42
|
+
this.onRectChange.subscribe(rect => {
|
43
|
+
this.context.fillStyle = rect.color
|
44
|
+
this.context.beginPath()
|
45
|
+
this.context.rect(Math.ceil(rect.x), Math.ceil(rect.y), Math.ceil(rect.width), Math.ceil(rect.height))
|
46
|
+
this.context.fill()
|
47
|
+
this.context.closePath()
|
48
|
+
})
|
49
|
+
}
|
50
|
+
|
51
|
+
draw(paths: RemoteSelection[]) {
|
52
|
+
this.canvas.width = this.container.offsetWidth
|
53
|
+
this.canvas.height = this.container.offsetHeight
|
54
|
+
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
55
|
+
|
56
|
+
paths.filter(i => {
|
57
|
+
return i.paths.start.length && i.paths.end.length
|
58
|
+
}).forEach(item => {
|
59
|
+
const startOffset = item.paths.start.pop()!
|
60
|
+
const startSlot = this.selection.findSlotByPaths(item.paths.start)
|
61
|
+
const endOffset = item.paths.end.pop()!
|
62
|
+
const endSlot = this.selection.findSlotByPaths(item.paths.end)
|
63
|
+
|
64
|
+
if (startSlot && endSlot) {
|
65
|
+
const position = this.nativeSelection.getPositionByRange({
|
66
|
+
startOffset,
|
67
|
+
endOffset,
|
68
|
+
startSlot,
|
69
|
+
endSlot
|
70
|
+
})
|
71
|
+
if (position.start && position.end) {
|
72
|
+
const nativeRange = this.document.createRange()
|
73
|
+
nativeRange.setStart(position.start.node, position.start.offset)
|
74
|
+
nativeRange.setEnd(position.end.node, position.end.offset)
|
75
|
+
|
76
|
+
const rects = nativeRange.getClientRects()
|
77
|
+
let prev: any = {}
|
78
|
+
for (let i = rects.length - 1; i >= 0; i--) {
|
79
|
+
const rect = rects[i]
|
80
|
+
if (prev.y === rect.y) {
|
81
|
+
continue
|
82
|
+
}
|
83
|
+
prev = rect
|
84
|
+
this.onRectChange.next({
|
85
|
+
x: rect.x,
|
86
|
+
y: rect.y,
|
87
|
+
width: rect.width,
|
88
|
+
height: rect.height,
|
89
|
+
color: item.color
|
90
|
+
})
|
91
|
+
}
|
92
|
+
|
93
|
+
if (rects.length === 0) {
|
94
|
+
const rect = nativeRange.getBoundingClientRect()
|
95
|
+
this.onRectChange.next({
|
96
|
+
x: rect.x,
|
97
|
+
y: rect.y,
|
98
|
+
width: 1,
|
99
|
+
height: rect.height,
|
100
|
+
color: item.color
|
101
|
+
})
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
})
|
106
|
+
}
|
107
|
+
}
|
@@ -0,0 +1,158 @@
|
|
1
|
+
import { Action, Operation, SlotLiteral, ComponentLiteral } from '@textbus/core'
|
2
|
+
import { Array as YArray, Map as YMap, Text as YText } from 'yjs'
|
3
|
+
|
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)
|
17
|
+
}
|
18
|
+
|
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
|
+
})
|
45
|
+
}
|
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
|
54
|
+
}
|
55
|
+
const content = slotYMap.get('content') as YText
|
56
|
+
|
57
|
+
this.mergeActionsToSharedSlot(content, actions)
|
58
|
+
}
|
59
|
+
|
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') {
|
66
|
+
if (action.formats) {
|
67
|
+
content.format(index, action.offset, action.formats)
|
68
|
+
} else {
|
69
|
+
index = action.offset
|
70
|
+
}
|
71
|
+
} else if (action.type === 'insert') {
|
72
|
+
const delta = content.toDelta()
|
73
|
+
const isEmpty = delta.length === 1 && delta[0].insert === '\n'
|
74
|
+
|
75
|
+
if (typeof action.content === 'string') {
|
76
|
+
length = action.content.length
|
77
|
+
content.insert(index, action.content)
|
78
|
+
} else {
|
79
|
+
length = 1
|
80
|
+
content.insertEmbed(index, this.makeSharedComponentByComponentLiteral(action.content))
|
81
|
+
}
|
82
|
+
if (action.formats) {
|
83
|
+
content.format(index, length, action.formats)
|
84
|
+
}
|
85
|
+
if (isEmpty && index === 0) {
|
86
|
+
content.delete(content.length - 1, 1)
|
87
|
+
}
|
88
|
+
index += length
|
89
|
+
} else if (action.type === 'delete') {
|
90
|
+
const delta = content.toDelta()
|
91
|
+
content.delete(index, action.count)
|
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
|
+
}
|
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
|
+
})
|
124
|
+
|
125
|
+
const sharedSlot = new YMap()
|
126
|
+
sharedSlot.set('content', content)
|
127
|
+
sharedSlot.set('schema', slotLiteral.schema)
|
128
|
+
return sharedSlot
|
129
|
+
}
|
130
|
+
|
131
|
+
private makeSharedComponentByComponentLiteral(componentLiteral: ComponentLiteral): YMap<any> {
|
132
|
+
const slots = new YArray()
|
133
|
+
componentLiteral.slots.forEach(item => {
|
134
|
+
slots.push([this.makeSharedSlotBySlotLiteral(item)])
|
135
|
+
})
|
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
|
+
}
|
142
|
+
|
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
|
150
|
+
}
|
151
|
+
i += action.insert instanceof YMap ? 1 : action.insert.length
|
152
|
+
} else {
|
153
|
+
throw new Error('xxx')
|
154
|
+
}
|
155
|
+
}
|
156
|
+
return null
|
157
|
+
}
|
158
|
+
}
|