@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.
@@ -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, FormatterList } from '@textbus/core';
13
- import { Doc as YDoc } from 'yjs';
14
- import { localToRemote } from './local-to-remote';
15
- import { remoteToLocal } from './remote-to-local';
16
- // const collaborateErrorFn = makeError('Collaborate')
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, formatterList, starter) {
19
- this.rootComponentRef = rootComponentRef;
20
- this.translator = translator;
21
- this.formatterList = formatterList;
22
- this.starter = starter;
23
- this.yDoc = new YDoc();
24
- this.subscriptions = [];
25
- this.updateFromSelf = true;
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.listen();
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
- listen() {
36
- const root = this.yDoc.getArray('content');
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, this.translator, this.formatterList);
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
- FormatterList,
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;AACvE,OAAO,EACL,gBAAgB,EAChB,OAAO,EAEP,UAAU,EACV,aAAa,EACd,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,sDAAsD;AAGtD,IAAa,WAAW,GAAxB,MAAa,WAAW;IAMtB,YAAoB,gBAAkC,EAClC,UAAsB,EACtB,aAA4B,EAC5B,OAAgB;QAHhB,qBAAgB,GAAhB,gBAAgB,CAAkB;QAClC,eAAU,GAAV,UAAU,CAAY;QACtB,kBAAa,GAAb,aAAa,CAAe;QAC5B,YAAO,GAAP,OAAO,CAAS;QARpC,SAAI,GAAG,IAAI,IAAI,EAAE,CAAA;QAET,kBAAa,GAAmB,EAAE,CAAA;QAClC,mBAAc,GAAG,IAAI,CAAA;IAM7B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,aAAa,CAAC,IAAI,CACrB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,MAAM,EAAE,CAAA;QACf,CAAC,CAAC,CACH,CAAA;IACH,CAAC;IAED,OAAO;QACL,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;IAClD,CAAC;IAEO,MAAM;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAE,CAAA;QAC1D,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE;YACvC,IAAI,WAAW,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,EAAE;gBACpC,OAAM;aACP;YACD,IAAI,CAAC,cAAc,GAAG,KAAK,CAAA;YAE3B,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;YAEhE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;QAC5B,CAAC,CAAC,CAAA;QACF,MAAM,UAAU,GAAgB,EAAE,CAAA;QAClC,IAAI,CAAC,aAAa,CAAC,IAAI,CACrB,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CACxD,MAAM,CAAC,GAAG,EAAE;YACV,OAAO,IAAI,CAAC,cAAc,CAAA;QAC5B,CAAC,CAAC,EACF,GAAG,CAAC,EAAE,CAAC,EAAE;YACP,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACrB,CAAC,CAAC,EACF,YAAY,CAAC,CAAC,CAAC,CAChB,CAAC,SAAS,CAAC,GAAG,EAAE;YACf,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;gBACtB,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;oBAC7B,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;gBAChC,CAAC,CAAC,CAAA;gBACF,UAAU,CAAC,MAAM,GAAG,CAAC,CAAA;YACvB,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QACf,CAAC,CAAC,CACH,CAAA;IACH,CAAC;CACF,CAAA;AAzDY,WAAW;IADvB,UAAU,EAAE;qCAO2B,gBAAgB;QACtB,UAAU;QACP,aAAa;QACnB,OAAO;GATzB,WAAW,CAyDvB;SAzDY,WAAW"}
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"]}
@@ -1 +1,2 @@
1
+ export * from './collab/_api';
1
2
  export * from './collaborate';
@@ -1,2 +1,3 @@
1
+ export * from './collab/_api';
1
2
  export * from './collaborate';
2
3
  //# sourceMappingURL=public-api.js.map
@@ -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.36",
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.11",
30
- "@textbus/core": "^2.0.0-alpha.36",
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
- "yjs": "^13.5.27",
33
- "y-protocols": "^1.0.5"
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": "536327fc2609d3f2bbf3b0720738d3ed918d612c"
47
+ "gitHead": "a3de1ffbf0b8527eff875cd415b5d8d16df91ecb"
47
48
  }
@@ -0,0 +1,3 @@
1
+ export * from './collaborate-cursor'
2
+ export * from './local-to-remote'
3
+ export * from './remote-to-local'
@@ -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
+ }