@textbus/collaborate 4.0.4 → 4.1.0-alpha.1
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-history.d.ts +36 -0
- package/bundles/collaborate.d.ts +44 -35
- package/bundles/connectors/hocuspocus-connector.d.ts +2 -1
- package/bundles/connectors/y-websocket-connector.d.ts +2 -1
- package/bundles/index.esm.js +765 -317
- package/bundles/index.js +764 -314
- package/bundles/multiple-doc-collab-history.d.ts +37 -0
- package/bundles/multiple-document-collaborate-module.d.ts +14 -0
- package/bundles/public-api.d.ts +4 -0
- package/bundles/sub-model-loader.d.ts +45 -0
- package/package.json +3 -3
package/bundles/index.esm.js
CHANGED
|
@@ -2,8 +2,8 @@ import { HocuspocusProvider } from '@hocuspocus/provider';
|
|
|
2
2
|
import { Subject, map, filter, Subscription } from '@tanbo/stream';
|
|
3
3
|
import { WebsocketProvider } from 'y-websocket';
|
|
4
4
|
import { Injectable, Inject, Optional } from '@viewfly/core';
|
|
5
|
-
import { makeError,
|
|
6
|
-
import { Doc,
|
|
5
|
+
import { makeError, ChangeOrigin, Slot, createObjectProxy, createArrayProxy, AsyncSlot, AsyncComponent, Component, Scheduler, Registry, Selection, HISTORY_STACK_SIZE, RootComponentRef, History } from '@textbus/core';
|
|
6
|
+
import { Doc, createAbsolutePositionFromRelativePosition, createRelativePositionFromTypeIndex, Map, Array as Array$1, Text, UndoManager } from 'yjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* 协作通信通用接口
|
|
@@ -112,6 +112,36 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
112
112
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
const subModelLoaderErrorFn = makeError('subModelLoaderError');
|
|
116
|
+
/**
|
|
117
|
+
* 子文档加载器
|
|
118
|
+
*/
|
|
119
|
+
class SubModelLoader {
|
|
120
|
+
}
|
|
121
|
+
let NonSubModelLoader = class NonSubModelLoader extends SubModelLoader {
|
|
122
|
+
createSubModelBySlot() {
|
|
123
|
+
throw subModelLoaderErrorFn('single document does not support async slot.');
|
|
124
|
+
}
|
|
125
|
+
createSubModelByComponent() {
|
|
126
|
+
throw subModelLoaderErrorFn('single document does not support async component.');
|
|
127
|
+
}
|
|
128
|
+
loadSubModelByComponent() {
|
|
129
|
+
throw subModelLoaderErrorFn('single document does not support async component.');
|
|
130
|
+
}
|
|
131
|
+
loadSubModelBySlot() {
|
|
132
|
+
throw subModelLoaderErrorFn('single document does not support async slot.');
|
|
133
|
+
}
|
|
134
|
+
getLoadedModelBySlot() {
|
|
135
|
+
throw subModelLoaderErrorFn('single document does not support async slot.');
|
|
136
|
+
}
|
|
137
|
+
getLoadedModelByComponent() {
|
|
138
|
+
throw subModelLoaderErrorFn('single document does not support async component.');
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
NonSubModelLoader = __decorate([
|
|
142
|
+
Injectable()
|
|
143
|
+
], NonSubModelLoader);
|
|
144
|
+
|
|
115
145
|
const collaborateErrorFn = makeError('Collaborate');
|
|
116
146
|
class SlotMap {
|
|
117
147
|
constructor() {
|
|
@@ -151,112 +181,96 @@ class SlotMap {
|
|
|
151
181
|
}
|
|
152
182
|
}
|
|
153
183
|
}
|
|
154
|
-
class CustomUndoManagerConfig {
|
|
155
|
-
}
|
|
156
184
|
let Collaborate = class Collaborate {
|
|
157
|
-
|
|
158
|
-
var _a;
|
|
159
|
-
return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
|
|
160
|
-
}
|
|
161
|
-
get canForward() {
|
|
162
|
-
var _a;
|
|
163
|
-
return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
|
|
164
|
-
}
|
|
165
|
-
constructor(stackSize, rootComponentRef, scheduler, registry, selection, undoManagerConfig) {
|
|
166
|
-
this.stackSize = stackSize;
|
|
167
|
-
this.rootComponentRef = rootComponentRef;
|
|
185
|
+
constructor(scheduler, registry, selection, subModelLoader) {
|
|
168
186
|
this.scheduler = scheduler;
|
|
169
187
|
this.registry = registry;
|
|
170
188
|
this.selection = selection;
|
|
171
|
-
this.
|
|
189
|
+
this.subModelLoader = subModelLoader;
|
|
172
190
|
this.yDoc = new Doc();
|
|
173
|
-
this.
|
|
174
|
-
this.forwardEvent = new Subject();
|
|
175
|
-
this.changeEvent = new Subject();
|
|
176
|
-
this.pushEvent = new Subject();
|
|
177
|
-
this.manager = null;
|
|
191
|
+
this.slotMap = new SlotMap();
|
|
178
192
|
this.subscriptions = [];
|
|
179
193
|
this.updateFromRemote = false;
|
|
180
|
-
this.
|
|
181
|
-
this.
|
|
182
|
-
this.slotMap = new SlotMap();
|
|
183
|
-
this.updateRemoteActions = [];
|
|
194
|
+
this.addSubModelEvent = new Subject();
|
|
195
|
+
this.updateRemoteActions = new WeakMap();
|
|
184
196
|
this.noRecord = {};
|
|
185
|
-
this.
|
|
186
|
-
this.index = 0;
|
|
187
|
-
this.onBack = this.backEvent.asObservable();
|
|
188
|
-
this.onForward = this.forwardEvent.asObservable();
|
|
189
|
-
this.onChange = this.changeEvent.asObservable();
|
|
190
|
-
this.onPush = this.pushEvent.asObservable();
|
|
191
|
-
this.onLocalChangesApplied = this.localChangesAppliedEvent.asObservable();
|
|
197
|
+
this.onAddSubModel = this.addSubModelEvent.asObservable();
|
|
192
198
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
after: this.getRelativeCursorLocation()
|
|
227
|
-
});
|
|
228
|
-
this.index++;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
this.index--;
|
|
233
|
-
}
|
|
234
|
-
if (manager.undoStack.length > this.stackSize) {
|
|
235
|
-
this.historyItems.shift();
|
|
236
|
-
manager.undoStack.shift();
|
|
237
|
-
}
|
|
238
|
-
if (event.origin === this.yDoc) {
|
|
239
|
-
this.pushEvent.next();
|
|
199
|
+
syncRootComponent(yDoc, sharedComponent, localComponent) {
|
|
200
|
+
this.initSyncEvent(yDoc);
|
|
201
|
+
this.syncComponent(yDoc, sharedComponent, localComponent);
|
|
202
|
+
}
|
|
203
|
+
syncRootSlot(yDoc, sharedSlot, localSlot) {
|
|
204
|
+
if (sharedSlot.length) {
|
|
205
|
+
localSlot.retain(0);
|
|
206
|
+
localSlot.delete(localSlot.length);
|
|
207
|
+
localSlot.cleanAttributes();
|
|
208
|
+
localSlot.cleanFormats();
|
|
209
|
+
this.initLocalSlotBySharedSlot(sharedSlot, localSlot);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
yDoc.transact(() => {
|
|
213
|
+
this.initSharedSlotByLocalSlot(sharedSlot, localSlot);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
this.initSyncEvent(yDoc);
|
|
217
|
+
this.syncSlot(sharedSlot, localSlot);
|
|
218
|
+
}
|
|
219
|
+
getAbstractSelection(position) {
|
|
220
|
+
const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor.position, position.anchor.doc);
|
|
221
|
+
const focusPosition = createAbsolutePositionFromRelativePosition(position.focus.position, position.focus.doc);
|
|
222
|
+
if (anchorPosition && focusPosition) {
|
|
223
|
+
const focusSlot = this.slotMap.get(focusPosition.type);
|
|
224
|
+
const anchorSlot = this.slotMap.get(anchorPosition.type);
|
|
225
|
+
if (focusSlot && anchorSlot) {
|
|
226
|
+
return {
|
|
227
|
+
anchorSlot,
|
|
228
|
+
anchorOffset: anchorPosition.index,
|
|
229
|
+
focusSlot,
|
|
230
|
+
focusOffset: focusPosition.index
|
|
231
|
+
};
|
|
240
232
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
getRelativeCursorLocation() {
|
|
237
|
+
const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
|
|
238
|
+
if (anchorSlot) {
|
|
239
|
+
const anchorYText = this.slotMap.get(anchorSlot);
|
|
240
|
+
if (anchorYText) {
|
|
241
|
+
const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
|
|
242
|
+
if (focusSlot) {
|
|
243
|
+
const focusYText = this.slotMap.get(focusSlot);
|
|
244
|
+
if (focusYText) {
|
|
245
|
+
const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset);
|
|
246
|
+
return {
|
|
247
|
+
focus: {
|
|
248
|
+
doc: focusYText.doc,
|
|
249
|
+
position: focusPosition
|
|
250
|
+
},
|
|
251
|
+
anchor: {
|
|
252
|
+
doc: anchorYText.doc,
|
|
253
|
+
position: anchorPosition
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
252
257
|
}
|
|
253
258
|
}
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
restoreCursorPosition(position) {
|
|
263
|
+
if (!position) {
|
|
254
264
|
this.selection.unSelect();
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const selection = this.getAbstractSelection(position);
|
|
268
|
+
if (selection) {
|
|
269
|
+
this.selection.setBaseAndExtent(selection.anchorSlot, selection.anchorOffset, selection.focusSlot, selection.focusOffset);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
initSyncEvent(yDoc) {
|
|
273
|
+
this.subscriptions.push(this.scheduler.onDocChanged.pipe(map(item => {
|
|
260
274
|
return item.filter(i => {
|
|
261
275
|
return i.from !== ChangeOrigin.Remote;
|
|
262
276
|
});
|
|
@@ -265,7 +279,8 @@ let Collaborate = class Collaborate {
|
|
|
265
279
|
})).subscribe(() => {
|
|
266
280
|
const updates = [];
|
|
267
281
|
let update = null;
|
|
268
|
-
|
|
282
|
+
const updateRemoteActions = this.updateRemoteActions.get(yDoc) || [];
|
|
283
|
+
for (const item of updateRemoteActions) {
|
|
269
284
|
if (!update) {
|
|
270
285
|
update = {
|
|
271
286
|
record: item.record,
|
|
@@ -284,218 +299,32 @@ let Collaborate = class Collaborate {
|
|
|
284
299
|
updates.push(update);
|
|
285
300
|
}
|
|
286
301
|
}
|
|
287
|
-
this.updateRemoteActions
|
|
302
|
+
this.updateRemoteActions.delete(yDoc);
|
|
288
303
|
for (const item of updates) {
|
|
289
|
-
|
|
304
|
+
yDoc.transact(() => {
|
|
290
305
|
item.actions.forEach(fn => {
|
|
291
306
|
fn();
|
|
292
307
|
});
|
|
293
|
-
}, item.record ?
|
|
308
|
+
}, item.record ? yDoc : this.noRecord);
|
|
294
309
|
}
|
|
295
|
-
this.localChangesAppliedEvent.next();
|
|
296
310
|
}));
|
|
297
|
-
this.syncRootComponent(root, rootComponent);
|
|
298
|
-
}
|
|
299
|
-
back() {
|
|
300
|
-
var _a;
|
|
301
|
-
if (this.canBack) {
|
|
302
|
-
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
|
|
303
|
-
this.backEvent.next();
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
forward() {
|
|
307
|
-
var _a;
|
|
308
|
-
if (this.canForward) {
|
|
309
|
-
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
|
|
310
|
-
this.forwardEvent.next();
|
|
311
|
-
}
|
|
312
311
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const last = this.historyItems.pop();
|
|
316
|
-
this.historyItems = last ? [last] : [];
|
|
317
|
-
this.index = last ? 1 : 0;
|
|
318
|
-
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
|
|
319
|
-
this.changeEvent.next();
|
|
320
|
-
}
|
|
321
|
-
destroy() {
|
|
322
|
-
var _a;
|
|
323
|
-
this.index = 0;
|
|
324
|
-
this.historyItems = [];
|
|
325
|
-
this.subscriptions.forEach(i => i.unsubscribe());
|
|
326
|
-
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
327
|
-
}
|
|
328
|
-
syncRootComponent(root, rootComponent) {
|
|
329
|
-
let state = root.get('state');
|
|
312
|
+
syncComponent(yDoc, sharedComponent, localComponent) {
|
|
313
|
+
let state = sharedComponent.get('state');
|
|
330
314
|
if (!state) {
|
|
331
315
|
state = new Map();
|
|
332
|
-
this.syncLocalMapToSharedMap(
|
|
333
|
-
|
|
334
|
-
|
|
316
|
+
this.syncLocalMapToSharedMap(localComponent.state, state);
|
|
317
|
+
yDoc.transact(() => {
|
|
318
|
+
sharedComponent.set('state', state);
|
|
335
319
|
});
|
|
336
320
|
}
|
|
337
321
|
else {
|
|
338
|
-
Object.keys(
|
|
339
|
-
Reflect.deleteProperty(
|
|
322
|
+
Object.keys(localComponent.state).forEach(key => {
|
|
323
|
+
Reflect.deleteProperty(localComponent.state, key);
|
|
340
324
|
});
|
|
341
|
-
this.syncSharedMapToLocalMap(state,
|
|
325
|
+
this.syncSharedMapToLocalMap(state, localComponent.state);
|
|
342
326
|
}
|
|
343
327
|
}
|
|
344
|
-
syncSharedMapToLocalMap(sharedMap, localMap) {
|
|
345
|
-
sharedMap.forEach((value, key) => {
|
|
346
|
-
localMap[key] = this.createLocalModelBySharedByModel(value);
|
|
347
|
-
});
|
|
348
|
-
this.syncObject(sharedMap, localMap);
|
|
349
|
-
}
|
|
350
|
-
createLocalMapBySharedMap(sharedMap) {
|
|
351
|
-
const localMap = createObjectProxy({});
|
|
352
|
-
this.syncSharedMapToLocalMap(sharedMap, localMap);
|
|
353
|
-
return localMap;
|
|
354
|
-
}
|
|
355
|
-
createLocalArrayBySharedArray(sharedArray) {
|
|
356
|
-
const localArray = createArrayProxy([]);
|
|
357
|
-
localArray.push(...sharedArray.map(item => this.createLocalModelBySharedByModel(item)));
|
|
358
|
-
this.syncArray(sharedArray, localArray);
|
|
359
|
-
return localArray;
|
|
360
|
-
}
|
|
361
|
-
syncLocalMapToSharedMap(localMap, sharedMap) {
|
|
362
|
-
Object.entries(localMap).forEach(([key, value]) => {
|
|
363
|
-
sharedMap.set(key, this.createSharedModelByLocalModel(value));
|
|
364
|
-
});
|
|
365
|
-
this.syncObject(sharedMap, localMap);
|
|
366
|
-
}
|
|
367
|
-
createSharedMapByLocalMap(localMap) {
|
|
368
|
-
const sharedMap = new Map();
|
|
369
|
-
this.syncLocalMapToSharedMap(localMap, sharedMap);
|
|
370
|
-
return sharedMap;
|
|
371
|
-
}
|
|
372
|
-
createSharedArrayByLocalArray(localArray) {
|
|
373
|
-
const sharedArray = new Array$1();
|
|
374
|
-
localArray.forEach(value => {
|
|
375
|
-
sharedArray.push([this.createSharedModelByLocalModel(value)]);
|
|
376
|
-
});
|
|
377
|
-
this.syncArray(sharedArray, localArray);
|
|
378
|
-
return sharedArray;
|
|
379
|
-
}
|
|
380
|
-
createSharedSlotByLocalSlot(localSlot) {
|
|
381
|
-
const sharedSlot = new Text();
|
|
382
|
-
sharedSlot.setAttribute('__schema__', [...localSlot.schema]);
|
|
383
|
-
let offset = 0;
|
|
384
|
-
localSlot.toDelta().forEach(i => {
|
|
385
|
-
let formats = {};
|
|
386
|
-
if (i.formats) {
|
|
387
|
-
i.formats.forEach(item => {
|
|
388
|
-
formats[item[0].name] = item[1];
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
formats = null;
|
|
393
|
-
}
|
|
394
|
-
if (typeof i.insert === 'string') {
|
|
395
|
-
sharedSlot.insert(offset, i.insert, formats);
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
const sharedComponent = this.createSharedComponentByLocalComponent(i.insert);
|
|
399
|
-
sharedSlot.insertEmbed(offset, sharedComponent, formats);
|
|
400
|
-
}
|
|
401
|
-
offset += i.insert.length;
|
|
402
|
-
});
|
|
403
|
-
localSlot.getAttributes().forEach(item => {
|
|
404
|
-
sharedSlot.setAttribute(item[0].name, item[1]);
|
|
405
|
-
});
|
|
406
|
-
this.syncSlot(sharedSlot, localSlot);
|
|
407
|
-
return sharedSlot;
|
|
408
|
-
}
|
|
409
|
-
createLocalSlotBySharedSlot(sharedSlot) {
|
|
410
|
-
const delta = sharedSlot.toDelta();
|
|
411
|
-
const localSlot = new Slot(sharedSlot.getAttribute('__schema__') || []); // TODO 这里有潜在的问题
|
|
412
|
-
const attrs = sharedSlot.getAttributes();
|
|
413
|
-
Object.keys(attrs).forEach(key => {
|
|
414
|
-
const attribute = this.registry.getAttribute(key);
|
|
415
|
-
if (attribute) {
|
|
416
|
-
localSlot.setAttribute(attribute, attrs[key]);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
for (const action of delta) {
|
|
420
|
-
if (action.insert) {
|
|
421
|
-
if (typeof action.insert === 'string') {
|
|
422
|
-
const formats = remoteFormatsToLocal(this.registry, action.attributes);
|
|
423
|
-
localSlot.insert(action.insert, formats);
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
const sharedComponent = action.insert;
|
|
427
|
-
const component = this.createLocalComponentBySharedComponent(sharedComponent);
|
|
428
|
-
localSlot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
throw collaborateErrorFn('unexpected delta action.');
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
this.syncSlot(sharedSlot, localSlot);
|
|
436
|
-
return localSlot;
|
|
437
|
-
}
|
|
438
|
-
createSharedModelByLocalModel(localModel) {
|
|
439
|
-
if (localModel instanceof Slot) {
|
|
440
|
-
return this.createSharedSlotByLocalSlot(localModel);
|
|
441
|
-
}
|
|
442
|
-
if (Array.isArray(localModel)) {
|
|
443
|
-
return this.createSharedArrayByLocalArray(localModel);
|
|
444
|
-
}
|
|
445
|
-
if (typeof localModel === 'object' && localModel !== null) {
|
|
446
|
-
return this.createSharedMapByLocalMap(localModel);
|
|
447
|
-
}
|
|
448
|
-
return localModel;
|
|
449
|
-
}
|
|
450
|
-
createLocalModelBySharedByModel(sharedModel) {
|
|
451
|
-
if (sharedModel instanceof Map) {
|
|
452
|
-
return this.createLocalMapBySharedMap(sharedModel);
|
|
453
|
-
}
|
|
454
|
-
if (sharedModel instanceof Array$1) {
|
|
455
|
-
return this.createLocalArrayBySharedArray(sharedModel);
|
|
456
|
-
}
|
|
457
|
-
if (sharedModel instanceof Text) {
|
|
458
|
-
return this.createLocalSlotBySharedSlot(sharedModel);
|
|
459
|
-
}
|
|
460
|
-
return sharedModel;
|
|
461
|
-
}
|
|
462
|
-
getAbstractSelection(position) {
|
|
463
|
-
const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
|
|
464
|
-
const focusPosition = createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
|
|
465
|
-
if (anchorPosition && focusPosition) {
|
|
466
|
-
const focusSlot = this.slotMap.get(focusPosition.type);
|
|
467
|
-
const anchorSlot = this.slotMap.get(anchorPosition.type);
|
|
468
|
-
if (focusSlot && anchorSlot) {
|
|
469
|
-
return {
|
|
470
|
-
anchorSlot,
|
|
471
|
-
anchorOffset: anchorPosition.index,
|
|
472
|
-
focusSlot,
|
|
473
|
-
focusOffset: focusPosition.index
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return null;
|
|
478
|
-
}
|
|
479
|
-
getRelativeCursorLocation() {
|
|
480
|
-
const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
|
|
481
|
-
if (anchorSlot) {
|
|
482
|
-
const anchorYText = this.slotMap.get(anchorSlot);
|
|
483
|
-
if (anchorYText) {
|
|
484
|
-
const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
|
|
485
|
-
if (focusSlot) {
|
|
486
|
-
const focusYText = this.slotMap.get(focusSlot);
|
|
487
|
-
if (focusYText) {
|
|
488
|
-
const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset);
|
|
489
|
-
return {
|
|
490
|
-
focus: focusPosition,
|
|
491
|
-
anchor: anchorPosition
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
328
|
syncSlot(sharedSlot, localSlot) {
|
|
500
329
|
const syncRemote = (ev, tr) => {
|
|
501
330
|
this.runRemoteUpdate(tr, () => {
|
|
@@ -544,7 +373,7 @@ let Collaborate = class Collaborate {
|
|
|
544
373
|
const component = this.createLocalComponentBySharedComponent(sharedComponent);
|
|
545
374
|
localSlot.insert(component);
|
|
546
375
|
}
|
|
547
|
-
if (this.selection.isSelected && tr.origin
|
|
376
|
+
if (this.selection.isSelected && !(tr.origin instanceof UndoManager)) {
|
|
548
377
|
if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
|
|
549
378
|
this.selection.setAnchor(localSlot, this.selection.anchorOffset + length);
|
|
550
379
|
}
|
|
@@ -556,7 +385,7 @@ let Collaborate = class Collaborate {
|
|
|
556
385
|
else if (action.delete) {
|
|
557
386
|
const index = localSlot.index;
|
|
558
387
|
localSlot.delete(action.delete);
|
|
559
|
-
if (this.selection.isSelected && tr.origin
|
|
388
|
+
if (this.selection.isSelected && !(tr.origin instanceof UndoManager)) {
|
|
560
389
|
if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
|
|
561
390
|
this.selection.setAnchor(localSlot, this.selection.startOffset - action.delete);
|
|
562
391
|
}
|
|
@@ -570,7 +399,7 @@ let Collaborate = class Collaborate {
|
|
|
570
399
|
};
|
|
571
400
|
sharedSlot.observe(syncRemote);
|
|
572
401
|
const sub = localSlot.onContentChange.subscribe(actions => {
|
|
573
|
-
this.runLocalUpdate(() => {
|
|
402
|
+
this.runLocalUpdate(sharedSlot.doc, true, () => {
|
|
574
403
|
var _a;
|
|
575
404
|
let offset = 0;
|
|
576
405
|
let length = 0;
|
|
@@ -628,7 +457,7 @@ let Collaborate = class Collaborate {
|
|
|
628
457
|
sharedSlot.removeAttribute(action.name);
|
|
629
458
|
}
|
|
630
459
|
}
|
|
631
|
-
}
|
|
460
|
+
});
|
|
632
461
|
});
|
|
633
462
|
this.slotMap.set(localSlot, sharedSlot);
|
|
634
463
|
localSlot.__changeMarker__.destroyCallbacks.push(() => {
|
|
@@ -637,18 +466,281 @@ let Collaborate = class Collaborate {
|
|
|
637
466
|
sub.unsubscribe();
|
|
638
467
|
});
|
|
639
468
|
}
|
|
469
|
+
destroy() {
|
|
470
|
+
this.subscriptions.forEach(i => i.unsubscribe());
|
|
471
|
+
}
|
|
472
|
+
syncSharedMapToLocalMap(sharedMap, localMap) {
|
|
473
|
+
sharedMap.forEach((value, key) => {
|
|
474
|
+
localMap[key] = this.createLocalModelBySharedByModel(value);
|
|
475
|
+
});
|
|
476
|
+
this.syncObject(sharedMap, localMap);
|
|
477
|
+
}
|
|
478
|
+
createLocalMapBySharedMap(sharedMap) {
|
|
479
|
+
const localMap = createObjectProxy({});
|
|
480
|
+
this.syncSharedMapToLocalMap(sharedMap, localMap);
|
|
481
|
+
return localMap;
|
|
482
|
+
}
|
|
483
|
+
createLocalArrayBySharedArray(sharedArray) {
|
|
484
|
+
const localArray = createArrayProxy([]);
|
|
485
|
+
localArray.push(...sharedArray.map(item => this.createLocalModelBySharedByModel(item)));
|
|
486
|
+
this.syncArray(sharedArray, localArray);
|
|
487
|
+
return localArray;
|
|
488
|
+
}
|
|
489
|
+
syncLocalMapToSharedMap(localMap, sharedMap) {
|
|
490
|
+
Object.entries(localMap).forEach(([key, value]) => {
|
|
491
|
+
sharedMap.set(key, this.createSharedModelByLocalModel(value));
|
|
492
|
+
});
|
|
493
|
+
this.syncObject(sharedMap, localMap);
|
|
494
|
+
}
|
|
495
|
+
createSharedMapByLocalMap(localMap) {
|
|
496
|
+
const sharedMap = new Map();
|
|
497
|
+
this.syncLocalMapToSharedMap(localMap, sharedMap);
|
|
498
|
+
return sharedMap;
|
|
499
|
+
}
|
|
500
|
+
createSharedArrayByLocalArray(localArray) {
|
|
501
|
+
const sharedArray = new Array$1();
|
|
502
|
+
localArray.forEach(value => {
|
|
503
|
+
sharedArray.push([this.createSharedModelByLocalModel(value)]);
|
|
504
|
+
});
|
|
505
|
+
this.syncArray(sharedArray, localArray);
|
|
506
|
+
return sharedArray;
|
|
507
|
+
}
|
|
508
|
+
createSharedSlotByLocalSlot(localSlot) {
|
|
509
|
+
const sharedSlot = new Text();
|
|
510
|
+
const isAsyncSlot = localSlot instanceof AsyncSlot;
|
|
511
|
+
sharedSlot.setAttribute('schema', [...localSlot.schema]);
|
|
512
|
+
sharedSlot.setAttribute('type', isAsyncSlot ? 'async' : 'sync');
|
|
513
|
+
if (isAsyncSlot) {
|
|
514
|
+
let isDestroyed = false;
|
|
515
|
+
sharedSlot.setAttribute('metadata', localSlot.metadata);
|
|
516
|
+
this.subModelLoader.createSubModelBySlot(localSlot).then(subDocument => {
|
|
517
|
+
if (isDestroyed) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const content = subDocument.getText('content');
|
|
521
|
+
this.initSharedSlotByLocalSlot(content, localSlot);
|
|
522
|
+
this.syncSlot(content, localSlot);
|
|
523
|
+
this.addSubModelEvent.next({
|
|
524
|
+
yDoc: subDocument,
|
|
525
|
+
yType: content
|
|
526
|
+
});
|
|
527
|
+
this.initSyncEvent(subDocument);
|
|
528
|
+
localSlot.loader.markAsLoaded();
|
|
529
|
+
});
|
|
530
|
+
localSlot.__changeMarker__.destroyCallbacks.push(() => {
|
|
531
|
+
isDestroyed = true;
|
|
532
|
+
});
|
|
533
|
+
return sharedSlot;
|
|
534
|
+
}
|
|
535
|
+
const sharedContent = new Text();
|
|
536
|
+
this.initSharedSlotByLocalSlot(sharedContent, localSlot);
|
|
537
|
+
sharedSlot.insertEmbed(0, sharedContent);
|
|
538
|
+
this.syncSlot(sharedContent, localSlot);
|
|
539
|
+
return sharedSlot;
|
|
540
|
+
}
|
|
541
|
+
initSharedSlotByLocalSlot(sharedContent, localSlot) {
|
|
542
|
+
let offset = 0;
|
|
543
|
+
localSlot.toDelta().forEach(i => {
|
|
544
|
+
let formats = {};
|
|
545
|
+
if (i.formats) {
|
|
546
|
+
i.formats.forEach(item => {
|
|
547
|
+
formats[item[0].name] = item[1];
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
formats = null;
|
|
552
|
+
}
|
|
553
|
+
if (typeof i.insert === 'string') {
|
|
554
|
+
sharedContent.insert(offset, i.insert, formats);
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
const sharedComponent = this.createSharedComponentByLocalComponent(i.insert);
|
|
558
|
+
sharedContent.insertEmbed(offset, sharedComponent, formats);
|
|
559
|
+
}
|
|
560
|
+
offset += i.insert.length;
|
|
561
|
+
});
|
|
562
|
+
localSlot.getAttributes().forEach(item => {
|
|
563
|
+
sharedContent.setAttribute(item[0].name, item[1]);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
createLocalSlotBySharedSlot(sharedSlot) {
|
|
567
|
+
var _a;
|
|
568
|
+
const type = sharedSlot.getAttribute('type');
|
|
569
|
+
const schema = sharedSlot.getAttribute('schema');
|
|
570
|
+
if (type === 'async') {
|
|
571
|
+
const metadata = sharedSlot.getAttribute('metadata');
|
|
572
|
+
const slot = new AsyncSlot(schema || [], metadata);
|
|
573
|
+
const loadedSubDocument = this.subModelLoader.getLoadedModelBySlot(slot);
|
|
574
|
+
if (loadedSubDocument) {
|
|
575
|
+
const subContent = loadedSubDocument.getText('content');
|
|
576
|
+
this.syncRootSlot(loadedSubDocument, subContent, slot);
|
|
577
|
+
this.addSubModelEvent.next({
|
|
578
|
+
yDoc: loadedSubDocument,
|
|
579
|
+
yType: subContent
|
|
580
|
+
});
|
|
581
|
+
slot.loader.markAsLoaded();
|
|
582
|
+
return slot;
|
|
583
|
+
}
|
|
584
|
+
let isDestroyed = false;
|
|
585
|
+
slot.loader.onRequestLoad.toPromise().then(() => {
|
|
586
|
+
return this.subModelLoader.loadSubModelBySlot(slot);
|
|
587
|
+
}).then(subDocument => {
|
|
588
|
+
if (isDestroyed) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
slot.loader.markAsLoaded();
|
|
592
|
+
const subContent = subDocument.getText('content');
|
|
593
|
+
this.syncRootSlot(subDocument, subContent, slot);
|
|
594
|
+
this.addSubModelEvent.next({
|
|
595
|
+
yDoc: subDocument,
|
|
596
|
+
yType: subContent
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
slot.__changeMarker__.destroyCallbacks.push(() => {
|
|
600
|
+
isDestroyed = true;
|
|
601
|
+
});
|
|
602
|
+
return slot;
|
|
603
|
+
}
|
|
604
|
+
const contentDelta = sharedSlot.toDelta();
|
|
605
|
+
const content = (_a = contentDelta[0]) === null || _a === void 0 ? void 0 : _a.insert;
|
|
606
|
+
if (!(content instanceof Text)) {
|
|
607
|
+
throw collaborateErrorFn('shared slot content type is not `YText`.');
|
|
608
|
+
}
|
|
609
|
+
const localSlot = new Slot(schema || []);
|
|
610
|
+
this.initLocalSlotBySharedSlot(content, localSlot);
|
|
611
|
+
this.syncSlot(content, localSlot);
|
|
612
|
+
return localSlot;
|
|
613
|
+
}
|
|
614
|
+
initLocalSlotBySharedSlot(content, localSlot) {
|
|
615
|
+
const delta = content.toDelta();
|
|
616
|
+
const attrs = content.getAttributes();
|
|
617
|
+
Object.keys(attrs).forEach(key => {
|
|
618
|
+
const attribute = this.registry.getAttribute(key);
|
|
619
|
+
if (attribute) {
|
|
620
|
+
localSlot.setAttribute(attribute, attrs[key]);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
for (const action of delta) {
|
|
624
|
+
if (action.insert) {
|
|
625
|
+
if (typeof action.insert === 'string') {
|
|
626
|
+
const formats = remoteFormatsToLocal(this.registry, action.attributes);
|
|
627
|
+
localSlot.insert(action.insert, formats);
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const sharedComponent = action.insert;
|
|
631
|
+
const component = this.createLocalComponentBySharedComponent(sharedComponent);
|
|
632
|
+
localSlot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
throw collaborateErrorFn('unexpected delta action.');
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
createSharedModelByLocalModel(localModel) {
|
|
641
|
+
if (localModel instanceof Slot) {
|
|
642
|
+
return this.createSharedSlotByLocalSlot(localModel);
|
|
643
|
+
}
|
|
644
|
+
if (Array.isArray(localModel)) {
|
|
645
|
+
return this.createSharedArrayByLocalArray(localModel);
|
|
646
|
+
}
|
|
647
|
+
if (typeof localModel === 'object' && localModel !== null) {
|
|
648
|
+
return this.createSharedMapByLocalMap(localModel);
|
|
649
|
+
}
|
|
650
|
+
return localModel;
|
|
651
|
+
}
|
|
652
|
+
createLocalModelBySharedByModel(sharedModel) {
|
|
653
|
+
if (sharedModel instanceof Map) {
|
|
654
|
+
return this.createLocalMapBySharedMap(sharedModel);
|
|
655
|
+
}
|
|
656
|
+
if (sharedModel instanceof Array$1) {
|
|
657
|
+
return this.createLocalArrayBySharedArray(sharedModel);
|
|
658
|
+
}
|
|
659
|
+
if (sharedModel instanceof Text) {
|
|
660
|
+
return this.createLocalSlotBySharedSlot(sharedModel);
|
|
661
|
+
}
|
|
662
|
+
return sharedModel;
|
|
663
|
+
}
|
|
640
664
|
createSharedComponentByLocalComponent(component) {
|
|
641
665
|
const sharedComponent = new Map();
|
|
642
|
-
const sharedState = this.createSharedMapByLocalMap(component.state);
|
|
643
666
|
sharedComponent.set('name', component.name);
|
|
667
|
+
if (component instanceof AsyncComponent) {
|
|
668
|
+
sharedComponent.set('type', 'async');
|
|
669
|
+
sharedComponent.set('metadata', component.getMetadata());
|
|
670
|
+
const state = component.state;
|
|
671
|
+
let isDestroyed = false;
|
|
672
|
+
state.__changeMarker__.destroyCallbacks.push(() => {
|
|
673
|
+
isDestroyed = true;
|
|
674
|
+
});
|
|
675
|
+
this.subModelLoader.createSubModelByComponent(component).then(subDocument => {
|
|
676
|
+
if (isDestroyed) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
const state = subDocument.getMap('state');
|
|
680
|
+
this.syncComponent(subDocument, state, component);
|
|
681
|
+
this.addSubModelEvent.next({
|
|
682
|
+
yType: state,
|
|
683
|
+
yDoc: subDocument
|
|
684
|
+
});
|
|
685
|
+
this.initSyncEvent(subDocument);
|
|
686
|
+
component.loader.markAsLoaded();
|
|
687
|
+
});
|
|
688
|
+
return sharedComponent;
|
|
689
|
+
}
|
|
690
|
+
const sharedState = this.createSharedMapByLocalMap(component.state);
|
|
644
691
|
sharedComponent.set('state', sharedState);
|
|
692
|
+
sharedComponent.set('type', 'sync');
|
|
645
693
|
return sharedComponent;
|
|
646
694
|
}
|
|
647
695
|
createLocalComponentBySharedComponent(yMap) {
|
|
648
696
|
const componentName = yMap.get('name');
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
697
|
+
const type = yMap.get('type');
|
|
698
|
+
let instance;
|
|
699
|
+
if (type === 'async') {
|
|
700
|
+
instance = this.registry.createComponentByData(componentName, {});
|
|
701
|
+
if (instance instanceof AsyncComponent) {
|
|
702
|
+
instance.setMetadata(yMap.get('metadata'));
|
|
703
|
+
const loadedSubDocument = this.subModelLoader.getLoadedModelByComponent(instance);
|
|
704
|
+
if (loadedSubDocument) {
|
|
705
|
+
const state = loadedSubDocument.getMap('state');
|
|
706
|
+
this.syncComponent(loadedSubDocument, state, instance);
|
|
707
|
+
this.addSubModelEvent.next({
|
|
708
|
+
yType: state,
|
|
709
|
+
yDoc: loadedSubDocument
|
|
710
|
+
});
|
|
711
|
+
instance.loader.markAsLoaded();
|
|
712
|
+
return instance;
|
|
713
|
+
}
|
|
714
|
+
const state = instance.state;
|
|
715
|
+
let isDestroyed = false;
|
|
716
|
+
instance.loader.onRequestLoad.toPromise().then(() => {
|
|
717
|
+
return this.subModelLoader.loadSubModelByComponent(instance);
|
|
718
|
+
})
|
|
719
|
+
.then(subDocument => {
|
|
720
|
+
if (isDestroyed) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
instance.loader.markAsLoaded();
|
|
724
|
+
const state = subDocument.getMap('state');
|
|
725
|
+
this.syncComponent(subDocument, state, instance);
|
|
726
|
+
this.addSubModelEvent.next({
|
|
727
|
+
yType: state,
|
|
728
|
+
yDoc: subDocument
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
state.__changeMarker__.destroyCallbacks.push(() => {
|
|
732
|
+
isDestroyed = true;
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
else if (instance instanceof Component) {
|
|
736
|
+
throw collaborateErrorFn(`component name \`${componentName}\` is not a async component.`);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
const sharedState = yMap.get('state');
|
|
741
|
+
const state = this.createLocalMapBySharedMap(sharedState);
|
|
742
|
+
instance = this.registry.createComponentByData(componentName, state);
|
|
743
|
+
}
|
|
652
744
|
if (instance) {
|
|
653
745
|
return instance;
|
|
654
746
|
}
|
|
@@ -662,7 +754,7 @@ let Collaborate = class Collaborate {
|
|
|
662
754
|
*/
|
|
663
755
|
syncArray(sharedArray, localArray) {
|
|
664
756
|
const sub = localArray.__changeMarker__.onSelfChange.subscribe((actions) => {
|
|
665
|
-
this.runLocalUpdate(() => {
|
|
757
|
+
this.runLocalUpdate(sharedArray.doc, !localArray.__changeMarker__.irrevocableUpdate, () => {
|
|
666
758
|
let index = 0;
|
|
667
759
|
for (const action of actions) {
|
|
668
760
|
switch (action.type) {
|
|
@@ -693,7 +785,7 @@ let Collaborate = class Collaborate {
|
|
|
693
785
|
break;
|
|
694
786
|
}
|
|
695
787
|
}
|
|
696
|
-
}
|
|
788
|
+
});
|
|
697
789
|
});
|
|
698
790
|
const syncRemote = (ev, tr) => {
|
|
699
791
|
this.runRemoteUpdate(tr, () => {
|
|
@@ -743,7 +835,7 @@ let Collaborate = class Collaborate {
|
|
|
743
835
|
};
|
|
744
836
|
sharedObject.observe(syncRemote);
|
|
745
837
|
const sub = localObject.__changeMarker__.onSelfChange.subscribe((actions) => {
|
|
746
|
-
this.runLocalUpdate(() => {
|
|
838
|
+
this.runLocalUpdate(sharedObject.doc, !localObject.__changeMarker__.irrevocableUpdate, () => {
|
|
747
839
|
for (const action of actions) {
|
|
748
840
|
switch (action.type) {
|
|
749
841
|
case 'propSet':
|
|
@@ -754,28 +846,33 @@ let Collaborate = class Collaborate {
|
|
|
754
846
|
break;
|
|
755
847
|
}
|
|
756
848
|
}
|
|
757
|
-
}
|
|
849
|
+
});
|
|
758
850
|
});
|
|
759
851
|
localObject.__changeMarker__.destroyCallbacks.push(function () {
|
|
760
852
|
sharedObject.unobserve(syncRemote);
|
|
761
853
|
sub.unsubscribe();
|
|
762
854
|
});
|
|
763
855
|
}
|
|
764
|
-
runLocalUpdate(
|
|
765
|
-
if (this.updateFromRemote) {
|
|
856
|
+
runLocalUpdate(yDoc, record, fn) {
|
|
857
|
+
if (this.updateFromRemote || !yDoc) {
|
|
766
858
|
return;
|
|
767
859
|
}
|
|
768
|
-
this.updateRemoteActions.
|
|
860
|
+
let changeList = this.updateRemoteActions.get(yDoc);
|
|
861
|
+
if (!changeList) {
|
|
862
|
+
changeList = [];
|
|
863
|
+
this.updateRemoteActions.set(yDoc, changeList);
|
|
864
|
+
}
|
|
865
|
+
changeList.push({
|
|
769
866
|
record,
|
|
770
867
|
action: fn
|
|
771
868
|
});
|
|
772
869
|
}
|
|
773
870
|
runRemoteUpdate(tr, fn) {
|
|
774
|
-
if (tr.origin ===
|
|
871
|
+
if (tr.origin === tr.doc) {
|
|
775
872
|
return;
|
|
776
873
|
}
|
|
777
874
|
this.updateFromRemote = true;
|
|
778
|
-
if (tr.origin
|
|
875
|
+
if (tr.origin instanceof UndoManager) {
|
|
779
876
|
this.scheduler.historyApplyTransact(fn);
|
|
780
877
|
}
|
|
781
878
|
else {
|
|
@@ -786,13 +883,10 @@ let Collaborate = class Collaborate {
|
|
|
786
883
|
};
|
|
787
884
|
Collaborate = __decorate([
|
|
788
885
|
Injectable(),
|
|
789
|
-
|
|
790
|
-
__param(5, Optional()),
|
|
791
|
-
__metadata("design:paramtypes", [Number, RootComponentRef,
|
|
792
|
-
Scheduler,
|
|
886
|
+
__metadata("design:paramtypes", [Scheduler,
|
|
793
887
|
Registry,
|
|
794
888
|
Selection,
|
|
795
|
-
|
|
889
|
+
SubModelLoader])
|
|
796
890
|
], Collaborate);
|
|
797
891
|
function remoteFormatsToLocal(registry, attrs) {
|
|
798
892
|
const formats = [];
|
|
@@ -807,6 +901,138 @@ function remoteFormatsToLocal(registry, attrs) {
|
|
|
807
901
|
return formats;
|
|
808
902
|
}
|
|
809
903
|
|
|
904
|
+
class CustomUndoManagerConfig {
|
|
905
|
+
}
|
|
906
|
+
const collabHistoryErrorFn = makeError('CollabHistory');
|
|
907
|
+
let CollabHistory = class CollabHistory {
|
|
908
|
+
get canBack() {
|
|
909
|
+
var _a;
|
|
910
|
+
return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canUndo()) || false;
|
|
911
|
+
}
|
|
912
|
+
get canForward() {
|
|
913
|
+
var _a;
|
|
914
|
+
return ((_a = this.manager) === null || _a === void 0 ? void 0 : _a.canRedo()) || false;
|
|
915
|
+
}
|
|
916
|
+
constructor(rootComponentRef, collaborate, scheduler, selection, stackSize, undoManagerConfig) {
|
|
917
|
+
this.rootComponentRef = rootComponentRef;
|
|
918
|
+
this.collaborate = collaborate;
|
|
919
|
+
this.scheduler = scheduler;
|
|
920
|
+
this.selection = selection;
|
|
921
|
+
this.stackSize = stackSize;
|
|
922
|
+
this.undoManagerConfig = undoManagerConfig;
|
|
923
|
+
this.manager = null;
|
|
924
|
+
this.historyItems = [];
|
|
925
|
+
this.index = 0;
|
|
926
|
+
this.subscriptions = [];
|
|
927
|
+
this.backEvent = new Subject();
|
|
928
|
+
this.forwardEvent = new Subject();
|
|
929
|
+
this.changeEvent = new Subject();
|
|
930
|
+
this.pushEvent = new Subject();
|
|
931
|
+
this.onBack = this.backEvent.asObservable();
|
|
932
|
+
this.onForward = this.forwardEvent.asObservable();
|
|
933
|
+
this.onChange = this.changeEvent.asObservable();
|
|
934
|
+
this.onPush = this.pushEvent.asObservable();
|
|
935
|
+
}
|
|
936
|
+
listen() {
|
|
937
|
+
const root = this.collaborate.yDoc.getMap('RootComponent');
|
|
938
|
+
const rootComponent = this.rootComponentRef.component;
|
|
939
|
+
this.collaborate.syncRootComponent(this.collaborate.yDoc, root, rootComponent);
|
|
940
|
+
const undoManagerConfig = this.undoManagerConfig || {};
|
|
941
|
+
const manager = new UndoManager(root, {
|
|
942
|
+
trackedOrigins: new Set([this.collaborate.yDoc]),
|
|
943
|
+
captureTransaction(arg) {
|
|
944
|
+
if (undoManagerConfig.captureTransaction) {
|
|
945
|
+
return undoManagerConfig.captureTransaction(arg);
|
|
946
|
+
}
|
|
947
|
+
return true;
|
|
948
|
+
},
|
|
949
|
+
deleteFilter(item) {
|
|
950
|
+
if (undoManagerConfig.deleteFilter) {
|
|
951
|
+
return undoManagerConfig.deleteFilter(item);
|
|
952
|
+
}
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
this.manager = manager;
|
|
957
|
+
let beforePosition = null;
|
|
958
|
+
this.subscriptions.push(this.scheduler.onLocalChangeBefore.subscribe(() => {
|
|
959
|
+
beforePosition = this.collaborate.getRelativeCursorLocation();
|
|
960
|
+
}), this.collaborate.onAddSubModel.subscribe(() => {
|
|
961
|
+
throw collabHistoryErrorFn('single document does not support submodels.');
|
|
962
|
+
}));
|
|
963
|
+
manager.on('stack-item-added', (event) => {
|
|
964
|
+
if (event.type === 'undo') {
|
|
965
|
+
if (event.origin === manager) {
|
|
966
|
+
this.index++;
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
this.historyItems.length = this.index;
|
|
970
|
+
this.historyItems.push({
|
|
971
|
+
before: beforePosition,
|
|
972
|
+
after: this.collaborate.getRelativeCursorLocation()
|
|
973
|
+
});
|
|
974
|
+
this.index++;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
this.index--;
|
|
979
|
+
}
|
|
980
|
+
if (manager.undoStack.length > this.stackSize) {
|
|
981
|
+
this.historyItems.shift();
|
|
982
|
+
manager.undoStack.shift();
|
|
983
|
+
}
|
|
984
|
+
if (event.origin === this.collaborate.yDoc) {
|
|
985
|
+
this.pushEvent.next();
|
|
986
|
+
}
|
|
987
|
+
this.changeEvent.next();
|
|
988
|
+
});
|
|
989
|
+
manager.on('stack-item-popped', (ev) => {
|
|
990
|
+
const index = ev.type === 'undo' ? this.index : this.index - 1;
|
|
991
|
+
const position = this.historyItems[index] || null;
|
|
992
|
+
const p = ev.type === 'undo' ? position === null || position === void 0 ? void 0 : position.before : position === null || position === void 0 ? void 0 : position.after;
|
|
993
|
+
this.collaborate.restoreCursorPosition(p);
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
back() {
|
|
997
|
+
var _a;
|
|
998
|
+
if (this.canBack) {
|
|
999
|
+
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.undo();
|
|
1000
|
+
this.backEvent.next();
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
forward() {
|
|
1004
|
+
var _a;
|
|
1005
|
+
if (this.canForward) {
|
|
1006
|
+
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.redo();
|
|
1007
|
+
this.forwardEvent.next();
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
clear() {
|
|
1011
|
+
var _a;
|
|
1012
|
+
const last = this.historyItems.pop();
|
|
1013
|
+
this.historyItems = last ? [last] : [];
|
|
1014
|
+
this.index = last ? 1 : 0;
|
|
1015
|
+
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.clear();
|
|
1016
|
+
this.changeEvent.next();
|
|
1017
|
+
}
|
|
1018
|
+
destroy() {
|
|
1019
|
+
var _a;
|
|
1020
|
+
this.index = 0;
|
|
1021
|
+
this.historyItems = [];
|
|
1022
|
+
this.subscriptions.forEach(i => i.unsubscribe());
|
|
1023
|
+
(_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
CollabHistory = __decorate([
|
|
1027
|
+
Injectable(),
|
|
1028
|
+
__param(4, Inject(HISTORY_STACK_SIZE)),
|
|
1029
|
+
__param(5, Optional()),
|
|
1030
|
+
__metadata("design:paramtypes", [RootComponentRef,
|
|
1031
|
+
Collaborate,
|
|
1032
|
+
Scheduler,
|
|
1033
|
+
Selection, Number, CustomUndoManagerConfig])
|
|
1034
|
+
], CollabHistory);
|
|
1035
|
+
|
|
810
1036
|
let UserActivity = class UserActivity {
|
|
811
1037
|
constructor(syncConnector, selection) {
|
|
812
1038
|
this.syncConnector = syncConnector;
|
|
@@ -855,15 +1081,235 @@ class CollaborateModule {
|
|
|
855
1081
|
this.providers = [
|
|
856
1082
|
Collaborate,
|
|
857
1083
|
UserActivity,
|
|
1084
|
+
CollabHistory,
|
|
1085
|
+
{
|
|
1086
|
+
provide: History,
|
|
1087
|
+
useExisting: CollabHistory
|
|
1088
|
+
}, {
|
|
1089
|
+
provide: SyncConnector,
|
|
1090
|
+
useFactory: (collab) => {
|
|
1091
|
+
return this.config.createConnector(collab.yDoc);
|
|
1092
|
+
},
|
|
1093
|
+
deps: [Collaborate]
|
|
1094
|
+
}, {
|
|
1095
|
+
provide: SubModelLoader,
|
|
1096
|
+
useClass: NonSubModelLoader
|
|
1097
|
+
}
|
|
1098
|
+
];
|
|
1099
|
+
}
|
|
1100
|
+
setup(textbus) {
|
|
1101
|
+
const connector = textbus.get(SyncConnector);
|
|
1102
|
+
const userActivity = textbus.get(UserActivity);
|
|
1103
|
+
userActivity.init(this.config.userinfo);
|
|
1104
|
+
return connector.onLoad.toPromise();
|
|
1105
|
+
}
|
|
1106
|
+
onDestroy(textbus) {
|
|
1107
|
+
textbus.get(Collaborate).destroy();
|
|
1108
|
+
textbus.get(History).destroy();
|
|
1109
|
+
textbus.get(UserActivity).destroy();
|
|
1110
|
+
textbus.get(SyncConnector).onDestroy();
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
let MultipleDocCollabHistory = class MultipleDocCollabHistory {
|
|
1115
|
+
get canBack() {
|
|
1116
|
+
return this.actionStack.length > 0 && this.index > 0;
|
|
1117
|
+
}
|
|
1118
|
+
get canForward() {
|
|
1119
|
+
return this.actionStack.length > 0 && this.index < this.actionStack.length;
|
|
1120
|
+
}
|
|
1121
|
+
constructor(collaborate, scheduler, rootComponentRef, stackSize, undoManagerConfig) {
|
|
1122
|
+
this.collaborate = collaborate;
|
|
1123
|
+
this.scheduler = scheduler;
|
|
1124
|
+
this.rootComponentRef = rootComponentRef;
|
|
1125
|
+
this.stackSize = stackSize;
|
|
1126
|
+
this.undoManagerConfig = undoManagerConfig;
|
|
1127
|
+
this.isListen = false;
|
|
1128
|
+
this.changeEvent = new Subject();
|
|
1129
|
+
this.backEvent = new Subject();
|
|
1130
|
+
this.forwardEvent = new Subject();
|
|
1131
|
+
this.pushEvent = new Subject();
|
|
1132
|
+
this.actionStack = [];
|
|
1133
|
+
this.index = 0;
|
|
1134
|
+
this.stackItem = null;
|
|
1135
|
+
this.timer = null;
|
|
1136
|
+
this.beforePosition = null;
|
|
1137
|
+
this.subscription = new Subscription();
|
|
1138
|
+
this.subDocs = new Set();
|
|
1139
|
+
this.listenerCaches = new Set();
|
|
1140
|
+
this.onChange = this.changeEvent.asObservable();
|
|
1141
|
+
this.onBack = this.backEvent.asObservable();
|
|
1142
|
+
this.onForward = this.forwardEvent.asObservable();
|
|
1143
|
+
this.onPush = this.pushEvent.asObservable();
|
|
1144
|
+
}
|
|
1145
|
+
listen() {
|
|
1146
|
+
this.isListen = true;
|
|
1147
|
+
const root = this.collaborate.yDoc.getMap('RootComponent');
|
|
1148
|
+
const rootComponent = this.rootComponentRef.component;
|
|
1149
|
+
this.collaborate.syncRootComponent(this.collaborate.yDoc, root, rootComponent);
|
|
1150
|
+
this.listenItem(root, this.collaborate.yDoc);
|
|
1151
|
+
this.subscription.add(this.collaborate.onAddSubModel.subscribe(({ yType, yDoc }) => {
|
|
1152
|
+
if (this.subDocs.has(yType)) {
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
this.subDocs.add(yType);
|
|
1156
|
+
if (this.isListen) {
|
|
1157
|
+
this.listenItem(yType, yDoc);
|
|
1158
|
+
}
|
|
1159
|
+
}), this.scheduler.onLocalChangeBefore.subscribe(() => {
|
|
1160
|
+
this.beforePosition = this.collaborate.getRelativeCursorLocation();
|
|
1161
|
+
}));
|
|
1162
|
+
}
|
|
1163
|
+
forward() {
|
|
1164
|
+
if (!this.canForward) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
clearTimeout(this.timer);
|
|
1168
|
+
const item = this.actionStack[this.index];
|
|
1169
|
+
if (item) {
|
|
1170
|
+
for (const i of item.undoManagers) {
|
|
1171
|
+
i.redo();
|
|
1172
|
+
}
|
|
1173
|
+
this.collaborate.restoreCursorPosition(item.after);
|
|
1174
|
+
}
|
|
1175
|
+
this.index++;
|
|
1176
|
+
this.forwardEvent.next();
|
|
1177
|
+
this.changeEvent.next();
|
|
1178
|
+
}
|
|
1179
|
+
back() {
|
|
1180
|
+
if (!this.canBack) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
clearTimeout(this.timer);
|
|
1184
|
+
let historyStackItem;
|
|
1185
|
+
if (this.stackItem) {
|
|
1186
|
+
historyStackItem = this.stackItem;
|
|
1187
|
+
this.stackItem = null;
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
this.index--;
|
|
1191
|
+
historyStackItem = this.actionStack[this.index];
|
|
1192
|
+
}
|
|
1193
|
+
let len = historyStackItem.undoManagers.length;
|
|
1194
|
+
while (len > 0) {
|
|
1195
|
+
len--;
|
|
1196
|
+
historyStackItem.undoManagers[len].undo();
|
|
1197
|
+
}
|
|
1198
|
+
if (historyStackItem) {
|
|
1199
|
+
const beforePosition = historyStackItem.before;
|
|
1200
|
+
this.collaborate.restoreCursorPosition(beforePosition);
|
|
1201
|
+
this.backEvent.next();
|
|
1202
|
+
this.changeEvent.next();
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
clear() {
|
|
1206
|
+
this.actionStack = [];
|
|
1207
|
+
this.stackItem = null;
|
|
1208
|
+
this.index = 0;
|
|
1209
|
+
this.beforePosition = null;
|
|
1210
|
+
clearTimeout(this.timer);
|
|
1211
|
+
this.listenerCaches.forEach((undoManager) => {
|
|
1212
|
+
undoManager.clear();
|
|
1213
|
+
});
|
|
1214
|
+
this.changeEvent.next();
|
|
1215
|
+
}
|
|
1216
|
+
destroy() {
|
|
1217
|
+
this.clear();
|
|
1218
|
+
this.beforePosition = this.stackItem = null;
|
|
1219
|
+
this.subscription.unsubscribe();
|
|
1220
|
+
this.listenerCaches.forEach((undoManager) => {
|
|
1221
|
+
undoManager.destroy();
|
|
1222
|
+
});
|
|
1223
|
+
this.subDocs.clear();
|
|
1224
|
+
this.listenerCaches.clear();
|
|
1225
|
+
}
|
|
1226
|
+
listenItem(yType, yDoc) {
|
|
1227
|
+
const undoManagerConfig = this.undoManagerConfig || {};
|
|
1228
|
+
const undoManager = new UndoManager(yType, {
|
|
1229
|
+
trackedOrigins: new Set([yDoc]),
|
|
1230
|
+
captureTimeout: 0,
|
|
1231
|
+
captureTransaction(arg) {
|
|
1232
|
+
if (undoManagerConfig.captureTransaction) {
|
|
1233
|
+
return undoManagerConfig.captureTransaction(arg);
|
|
1234
|
+
}
|
|
1235
|
+
return true;
|
|
1236
|
+
},
|
|
1237
|
+
deleteFilter(item) {
|
|
1238
|
+
if (undoManagerConfig.deleteFilter) {
|
|
1239
|
+
return undoManagerConfig.deleteFilter(item);
|
|
1240
|
+
}
|
|
1241
|
+
return true;
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
undoManager.on('stack-item-added', (event) => {
|
|
1245
|
+
if (event.type === 'undo' && !(event.origin instanceof UndoManager)) {
|
|
1246
|
+
if (this.index != this.actionStack.length) {
|
|
1247
|
+
const redoStack = this.actionStack.slice(this.index);
|
|
1248
|
+
redoStack.forEach(item => {
|
|
1249
|
+
item.undoManagers.forEach(i => {
|
|
1250
|
+
i.clear(false, true);
|
|
1251
|
+
});
|
|
1252
|
+
});
|
|
1253
|
+
this.actionStack.length = this.index;
|
|
1254
|
+
this.changeEvent.next();
|
|
1255
|
+
}
|
|
1256
|
+
if (this.stackItem === null) {
|
|
1257
|
+
this.stackItem = {
|
|
1258
|
+
before: this.beforePosition,
|
|
1259
|
+
after: null,
|
|
1260
|
+
undoManagers: []
|
|
1261
|
+
};
|
|
1262
|
+
this.timer = setTimeout(() => {
|
|
1263
|
+
if (this.actionStack.length >= this.stackSize) {
|
|
1264
|
+
this.actionStack.shift();
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
this.index++;
|
|
1268
|
+
}
|
|
1269
|
+
// this.beforePosition = this.collaborate.getRelativeCursorLocation()
|
|
1270
|
+
this.stackItem.after = this.beforePosition;
|
|
1271
|
+
this.actionStack.push(this.stackItem);
|
|
1272
|
+
this.stackItem = null;
|
|
1273
|
+
this.pushEvent.next();
|
|
1274
|
+
this.changeEvent.next();
|
|
1275
|
+
}, 500);
|
|
1276
|
+
}
|
|
1277
|
+
this.stackItem.undoManagers.push(undoManager);
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
this.listenerCaches.add(undoManager);
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
MultipleDocCollabHistory = __decorate([
|
|
1284
|
+
Injectable(),
|
|
1285
|
+
__param(3, Inject(HISTORY_STACK_SIZE)),
|
|
1286
|
+
__param(4, Optional()),
|
|
1287
|
+
__metadata("design:paramtypes", [Collaborate,
|
|
1288
|
+
Scheduler,
|
|
1289
|
+
RootComponentRef, Number, CustomUndoManagerConfig])
|
|
1290
|
+
], MultipleDocCollabHistory);
|
|
1291
|
+
|
|
1292
|
+
class MultipleDocumentCollaborateModule {
|
|
1293
|
+
constructor(config) {
|
|
1294
|
+
this.config = config;
|
|
1295
|
+
this.providers = [
|
|
1296
|
+
Collaborate,
|
|
1297
|
+
UserActivity,
|
|
1298
|
+
MultipleDocCollabHistory,
|
|
858
1299
|
{
|
|
859
1300
|
provide: History,
|
|
860
|
-
useExisting:
|
|
1301
|
+
useExisting: MultipleDocCollabHistory
|
|
861
1302
|
}, {
|
|
862
1303
|
provide: SyncConnector,
|
|
863
1304
|
useFactory: (collab) => {
|
|
864
1305
|
return this.config.createConnector(collab.yDoc);
|
|
865
1306
|
},
|
|
866
1307
|
deps: [Collaborate]
|
|
1308
|
+
}, {
|
|
1309
|
+
provide: SubModelLoader,
|
|
1310
|
+
useFactory: () => {
|
|
1311
|
+
return this.config.subModelLoader;
|
|
1312
|
+
}
|
|
867
1313
|
}
|
|
868
1314
|
];
|
|
869
1315
|
}
|
|
@@ -874,9 +1320,11 @@ class CollaborateModule {
|
|
|
874
1320
|
return connector.onLoad.toPromise();
|
|
875
1321
|
}
|
|
876
1322
|
onDestroy(textbus) {
|
|
1323
|
+
textbus.get(Collaborate).destroy();
|
|
1324
|
+
textbus.get(History).destroy();
|
|
877
1325
|
textbus.get(UserActivity).destroy();
|
|
878
1326
|
textbus.get(SyncConnector).onDestroy();
|
|
879
1327
|
}
|
|
880
1328
|
}
|
|
881
1329
|
|
|
882
|
-
export { Collaborate, CollaborateModule, CustomUndoManagerConfig, HocuspocusConnector, SyncConnector, UserActivity, YWebsocketConnector };
|
|
1330
|
+
export { CollabHistory, Collaborate, CollaborateModule, CustomUndoManagerConfig, HocuspocusConnector, MultipleDocCollabHistory, MultipleDocumentCollaborateModule, NonSubModelLoader, SubModelLoader, SyncConnector, UserActivity, YWebsocketConnector };
|