@textbus/collaborate 2.0.0-beta.2 → 2.0.0-beta.22
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/collaborate-cursor.d.ts → collaborate-cursor.d.ts} +11 -6
- package/bundles/collaborate-cursor.js +265 -0
- package/bundles/collaborate.d.ts +5 -5
- package/bundles/collaborate.js +58 -43
- package/bundles/fixed-caret.plugin.d.ts +9 -0
- package/bundles/fixed-caret.plugin.js +41 -0
- package/bundles/public-api.d.ts +4 -1
- package/bundles/public-api.js +16 -2
- package/bundles/unknown.component.d.ts +1 -0
- package/bundles/unknown.component.js +22 -0
- package/package.json +6 -6
- package/src/{collab/collaborate-cursor.ts → collaborate-cursor.ts} +77 -53
- package/src/collaborate.ts +63 -52
- package/src/fixed-caret.plugin.ts +35 -0
- package/src/public-api.ts +18 -1
- package/src/unknown.component.ts +22 -0
- package/bundles/collab/_api.d.ts +0 -1
- package/bundles/collab/_api.js +0 -2
- package/bundles/collab/collaborate-cursor.js +0 -245
- package/src/collab/_api.ts +0 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@textbus/collaborate",
|
3
|
-
"version": "2.0.0-beta.
|
3
|
+
"version": "2.0.0-beta.22",
|
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",
|
@@ -25,10 +25,10 @@
|
|
25
25
|
"typescript editor"
|
26
26
|
],
|
27
27
|
"dependencies": {
|
28
|
-
"@tanbo/di": "^1.1.
|
29
|
-
"@tanbo/stream": "^1.0.
|
30
|
-
"@textbus/browser": "^2.0.0-beta.
|
31
|
-
"@textbus/core": "^2.0.0-beta.
|
28
|
+
"@tanbo/di": "^1.1.1",
|
29
|
+
"@tanbo/stream": "^1.0.2",
|
30
|
+
"@textbus/browser": "^2.0.0-beta.22",
|
31
|
+
"@textbus/core": "^2.0.0-beta.22",
|
32
32
|
"reflect-metadata": "^0.1.13",
|
33
33
|
"y-protocols": "^1.0.5",
|
34
34
|
"yjs": "^13.5.27"
|
@@ -44,5 +44,5 @@
|
|
44
44
|
"bugs": {
|
45
45
|
"url": "https://github.com/textbus/textbus.git/issues"
|
46
46
|
},
|
47
|
-
"gitHead": "
|
47
|
+
"gitHead": "2ee52d09e59875e4e8fccd769ef395d4fe373b09"
|
48
48
|
}
|
@@ -1,12 +1,11 @@
|
|
1
|
-
import { Inject, Injectable } from '@tanbo/di'
|
1
|
+
import { Inject, Injectable, Optional } from '@tanbo/di'
|
2
2
|
import {
|
3
3
|
createElement,
|
4
|
-
EDITABLE_DOCUMENT,
|
5
4
|
EDITOR_CONTAINER,
|
6
5
|
getLayoutRectByRange,
|
7
6
|
SelectionBridge
|
8
7
|
} from '@textbus/browser'
|
9
|
-
import { Selection, SelectionPaths } from '@textbus/core'
|
8
|
+
import { Selection, SelectionPaths, Range as TBRange } from '@textbus/core'
|
10
9
|
import { Subject } from '@tanbo/stream'
|
11
10
|
|
12
11
|
export interface RemoteSelection {
|
@@ -15,21 +14,28 @@ export interface RemoteSelection {
|
|
15
14
|
paths: SelectionPaths
|
16
15
|
}
|
17
16
|
|
18
|
-
export interface
|
19
|
-
color: string
|
20
|
-
username: string
|
17
|
+
export interface Rect {
|
21
18
|
x: number
|
22
19
|
y: number
|
23
20
|
width: number
|
24
21
|
height: number
|
25
22
|
}
|
26
23
|
|
24
|
+
export interface SelectionRect extends Rect {
|
25
|
+
color: string
|
26
|
+
username: string
|
27
|
+
}
|
28
|
+
|
27
29
|
export interface RemoteSelectionCursor {
|
28
30
|
cursor: HTMLElement
|
29
31
|
anchor: HTMLElement
|
30
32
|
userTip: HTMLElement
|
31
33
|
}
|
32
34
|
|
35
|
+
export abstract class CollaborateCursorAwarenessDelegate {
|
36
|
+
abstract getRects(range: TBRange, nativeRange: Range): false | Rect[]
|
37
|
+
}
|
38
|
+
|
33
39
|
@Injectable()
|
34
40
|
export class CollaborateCursor {
|
35
41
|
private canvas = createElement('canvas', {
|
@@ -60,7 +66,7 @@ export class CollaborateCursor {
|
|
60
66
|
private onRectsChange = new Subject<SelectionRect[]>()
|
61
67
|
|
62
68
|
constructor(@Inject(EDITOR_CONTAINER) private container: HTMLElement,
|
63
|
-
@
|
69
|
+
@Optional() private awarenessDelegate: CollaborateCursorAwarenessDelegate,
|
64
70
|
private nativeSelection: SelectionBridge,
|
65
71
|
private selection: Selection) {
|
66
72
|
container.prepend(this.canvas, this.tooltips)
|
@@ -85,55 +91,73 @@ export class CollaborateCursor {
|
|
85
91
|
|
86
92
|
|
87
93
|
paths.filter(i => {
|
88
|
-
return i.paths.
|
94
|
+
return i.paths.anchor.length && i.paths.focus.length
|
89
95
|
}).forEach(item => {
|
90
|
-
const
|
91
|
-
const
|
92
|
-
const
|
93
|
-
const
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
const anchorOffset = item.paths.anchor.pop()!
|
97
|
+
const anchorSlot = this.selection.findSlotByPaths(item.paths.anchor)
|
98
|
+
const focusOffset = item.paths.focus.pop()!
|
99
|
+
const focusSlot = this.selection.findSlotByPaths(item.paths.focus)
|
100
|
+
if (!anchorSlot || !focusSlot) {
|
101
|
+
return
|
102
|
+
}
|
103
|
+
|
104
|
+
const {focus, anchor} = this.nativeSelection.getPositionByRange({
|
105
|
+
focusOffset,
|
106
|
+
anchorOffset,
|
107
|
+
focusSlot,
|
108
|
+
anchorSlot
|
109
|
+
})
|
110
|
+
if (!focus || !anchor) {
|
111
|
+
return
|
112
|
+
}
|
113
|
+
const nativeRange = document.createRange()
|
114
|
+
nativeRange.setStart(anchor.node, anchor.offset)
|
115
|
+
nativeRange.setEnd(focus.node, focus.offset)
|
116
|
+
if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
|
117
|
+
nativeRange.setStart(focus.node, focus.offset)
|
118
|
+
nativeRange.setEnd(anchor.node, anchor.offset)
|
119
|
+
}
|
120
|
+
|
121
|
+
let rects: Rect[] | DOMRectList | false = false
|
122
|
+
if (this.awarenessDelegate) {
|
123
|
+
rects = this.awarenessDelegate.getRects({
|
124
|
+
focusOffset,
|
125
|
+
anchorOffset,
|
126
|
+
focusSlot,
|
127
|
+
anchorSlot
|
128
|
+
}, nativeRange)
|
129
|
+
}
|
130
|
+
if (!rects) {
|
131
|
+
rects = nativeRange.getClientRects()
|
132
|
+
}
|
133
|
+
const selectionRects: SelectionRect[] = []
|
134
|
+
for (let i = rects.length - 1; i >= 0; i--) {
|
135
|
+
const rect = rects[i]
|
136
|
+
selectionRects.push({
|
137
|
+
color: item.color,
|
138
|
+
username: item.username,
|
139
|
+
x: rect.x - containerRect.x,
|
140
|
+
y: rect.y - containerRect.y,
|
141
|
+
width: rect.width,
|
142
|
+
height: rect.height,
|
101
143
|
})
|
102
|
-
if (position.start && position.end) {
|
103
|
-
const nativeRange = this.document.createRange()
|
104
|
-
nativeRange.setStart(position.start.node, position.start.offset)
|
105
|
-
nativeRange.setEnd(position.end.node, position.end.offset)
|
106
|
-
|
107
|
-
const rects = nativeRange.getClientRects()
|
108
|
-
const selectionRects: SelectionRect[] = []
|
109
|
-
for (let i = rects.length - 1; i >= 0; i--) {
|
110
|
-
const rect = rects[i]
|
111
|
-
selectionRects.push({
|
112
|
-
color: item.color,
|
113
|
-
username: item.username,
|
114
|
-
x: rect.x - containerRect.x,
|
115
|
-
y: rect.y - containerRect.y,
|
116
|
-
width: rect.width,
|
117
|
-
height: rect.height,
|
118
|
-
})
|
119
|
-
}
|
120
|
-
this.onRectsChange.next(selectionRects)
|
121
|
-
|
122
|
-
const cursorRange = nativeRange.cloneRange()
|
123
|
-
cursorRange.collapse(!item.paths.focusEnd)
|
124
|
-
|
125
|
-
const cursorRect = getLayoutRectByRange(cursorRange)
|
126
|
-
|
127
|
-
users.push({
|
128
|
-
username: item.username,
|
129
|
-
color: item.color,
|
130
|
-
x: cursorRect.x - containerRect.x,
|
131
|
-
y: cursorRect.y - containerRect.y,
|
132
|
-
width: 2,
|
133
|
-
height: cursorRect.height
|
134
|
-
})
|
135
|
-
}
|
136
144
|
}
|
145
|
+
this.onRectsChange.next(selectionRects)
|
146
|
+
|
147
|
+
const cursorRange = nativeRange.cloneRange()
|
148
|
+
cursorRange.setStart(focus.node, focus.offset)
|
149
|
+
cursorRange.collapse(true)
|
150
|
+
|
151
|
+
const cursorRect = getLayoutRectByRange(cursorRange)
|
152
|
+
|
153
|
+
users.push({
|
154
|
+
username: item.username,
|
155
|
+
color: item.color,
|
156
|
+
x: cursorRect.x - containerRect.x,
|
157
|
+
y: cursorRect.y - containerRect.y,
|
158
|
+
width: 2,
|
159
|
+
height: cursorRect.height
|
160
|
+
})
|
137
161
|
})
|
138
162
|
this.drawUserCursor(users)
|
139
163
|
}
|
package/src/collaborate.ts
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
import { Injectable } from '@tanbo/di'
|
2
|
-
import {
|
2
|
+
import { delay, Observable, Subject, Subscription } from '@tanbo/stream'
|
3
3
|
import {
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
ComponentInstance,
|
5
|
+
ContentType,
|
6
|
+
Formats,
|
7
|
+
History,
|
8
|
+
makeError,
|
7
9
|
Registry,
|
10
|
+
RootComponentRef,
|
11
|
+
Scheduler,
|
8
12
|
Selection,
|
9
13
|
SelectionPaths,
|
10
|
-
|
14
|
+
Slot,
|
15
|
+
Starter,
|
16
|
+
Translator
|
11
17
|
} from '@textbus/core'
|
12
|
-
import {
|
13
|
-
Doc as YDoc,
|
14
|
-
Map as YMap,
|
15
|
-
Text as YText,
|
16
|
-
Array as YArray,
|
17
|
-
UndoManager,
|
18
|
-
Transaction
|
19
|
-
} from 'yjs'
|
18
|
+
import { Array as YArray, Doc as YDoc, Map as YMap, Text as YText, Transaction, UndoManager } from 'yjs'
|
20
19
|
|
21
|
-
import { CollaborateCursor, RemoteSelection } from './
|
20
|
+
import { CollaborateCursor, RemoteSelection } from './collaborate-cursor'
|
21
|
+
import { createUnknownComponent } from './unknown.component'
|
22
22
|
|
23
23
|
const collaborateErrorFn = makeError('Collaborate')
|
24
24
|
|
@@ -60,12 +60,12 @@ export class Collaborate implements History {
|
|
60
60
|
|
61
61
|
constructor(private rootComponentRef: RootComponentRef,
|
62
62
|
private collaborateCursor: CollaborateCursor,
|
63
|
+
private scheduler: Scheduler,
|
63
64
|
private translator: Translator,
|
64
|
-
private renderer: Renderer,
|
65
65
|
private registry: Registry,
|
66
66
|
private selection: Selection,
|
67
67
|
private starter: Starter) {
|
68
|
-
this.onSelectionChange = this.selectionChangeEvent.asObservable()
|
68
|
+
this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(delay())
|
69
69
|
this.onBack = this.backEvent.asObservable()
|
70
70
|
this.onForward = this.forwardEvent.asObservable()
|
71
71
|
this.onChange = this.changeEvent.asObservable()
|
@@ -74,14 +74,12 @@ export class Collaborate implements History {
|
|
74
74
|
|
75
75
|
setup() {
|
76
76
|
this.subscriptions.push(
|
77
|
-
this.starter.onReady.subscribe(() => {
|
78
|
-
this.listen2()
|
79
|
-
}),
|
80
77
|
this.selection.onChange.subscribe(() => {
|
81
78
|
const paths = this.selection.getPaths()
|
82
79
|
this.selectionChangeEvent.next(paths)
|
83
80
|
})
|
84
81
|
)
|
82
|
+
this.syncRootComponent()
|
85
83
|
}
|
86
84
|
|
87
85
|
updateRemoteSelection(paths: RemoteSelection[]) {
|
@@ -94,13 +92,17 @@ export class Collaborate implements History {
|
|
94
92
|
|
95
93
|
back() {
|
96
94
|
if (this.canBack) {
|
95
|
+
this.scheduler.stopBroadcastChanges = true
|
97
96
|
this.manager.undo()
|
97
|
+
this.scheduler.stopBroadcastChanges = false
|
98
98
|
}
|
99
99
|
}
|
100
100
|
|
101
101
|
forward() {
|
102
102
|
if (this.canForward) {
|
103
|
+
this.scheduler.stopBroadcastChanges = true
|
103
104
|
this.manager.redo()
|
105
|
+
this.scheduler.stopBroadcastChanges = false
|
104
106
|
}
|
105
107
|
}
|
106
108
|
|
@@ -108,7 +110,7 @@ export class Collaborate implements History {
|
|
108
110
|
this.subscriptions.forEach(i => i.unsubscribe())
|
109
111
|
}
|
110
112
|
|
111
|
-
private
|
113
|
+
private syncRootComponent() {
|
112
114
|
const root = this.yDoc.getText('content')
|
113
115
|
const rootComponent = this.rootComponentRef.component!
|
114
116
|
this.manager = new UndoManager(root, {
|
@@ -117,20 +119,13 @@ export class Collaborate implements History {
|
|
117
119
|
this.syncContent(root, rootComponent.slots.get(0)!)
|
118
120
|
|
119
121
|
this.subscriptions.push(
|
120
|
-
|
121
|
-
rootComponent.changeMarker.onForceChange,
|
122
|
-
rootComponent.changeMarker.onChange
|
123
|
-
).pipe(
|
124
|
-
microTask()
|
125
|
-
).subscribe(() => {
|
122
|
+
this.scheduler.onDocChange.subscribe(() => {
|
126
123
|
this.yDoc.transact(() => {
|
127
124
|
this.updateRemoteActions.forEach(fn => {
|
128
125
|
fn()
|
129
126
|
})
|
130
127
|
this.updateRemoteActions = []
|
131
128
|
}, this.yDoc)
|
132
|
-
this.renderer.render()
|
133
|
-
this.selection.restore()
|
134
129
|
})
|
135
130
|
)
|
136
131
|
}
|
@@ -157,17 +152,18 @@ export class Collaborate implements History {
|
|
157
152
|
slot.insert(action.insert, makeFormats(this.registry, action.attributes))
|
158
153
|
} else {
|
159
154
|
const sharedComponent = action.insert as YMap<any>
|
160
|
-
const
|
155
|
+
const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent)
|
156
|
+
const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent)
|
161
157
|
this.syncSlots(sharedComponent.get('slots'), component)
|
162
158
|
this.syncComponent(sharedComponent, component)
|
163
159
|
slot.insert(component)
|
164
160
|
}
|
165
161
|
if (this.selection.isSelected) {
|
166
|
-
if (slot === this.selection.
|
167
|
-
this.selection.
|
162
|
+
if (slot === this.selection.anchorSlot && this.selection.anchorOffset! > index) {
|
163
|
+
this.selection.setAnchor(slot, this.selection.anchorOffset! + length)
|
168
164
|
}
|
169
|
-
if (slot === this.selection.
|
170
|
-
this.selection.
|
165
|
+
if (slot === this.selection.focusSlot && this.selection.focusOffset! > index) {
|
166
|
+
this.selection.setFocus(slot, this.selection.focusOffset! + length)
|
171
167
|
}
|
172
168
|
}
|
173
169
|
} else if (action.delete) {
|
@@ -175,11 +171,11 @@ export class Collaborate implements History {
|
|
175
171
|
slot.retain(slot.index)
|
176
172
|
slot.delete(action.delete)
|
177
173
|
if (this.selection.isSelected) {
|
178
|
-
if (slot === this.selection.
|
179
|
-
this.selection.
|
174
|
+
if (slot === this.selection.anchorSlot && this.selection.anchorOffset! >= index) {
|
175
|
+
this.selection.setAnchor(slot, this.selection.startOffset! - action.delete)
|
180
176
|
}
|
181
|
-
if (slot === this.selection.
|
182
|
-
this.selection.
|
177
|
+
if (slot === this.selection.focusSlot && this.selection.focusOffset! >= index) {
|
178
|
+
this.selection.setFocus(slot, this.selection.focusOffset! - action.delete)
|
183
179
|
}
|
184
180
|
}
|
185
181
|
} else if (action.attributes) {
|
@@ -219,16 +215,14 @@ export class Collaborate implements History {
|
|
219
215
|
const isEmpty = delta.length === 1 && delta[0].insert === Slot.emptyPlaceholder
|
220
216
|
if (typeof action.content === 'string') {
|
221
217
|
length = action.content.length
|
222
|
-
content.insert(offset, action.content)
|
218
|
+
content.insert(offset, action.content, action.formats || {})
|
223
219
|
} else {
|
224
220
|
length = 1
|
225
221
|
const component = slot.getContentAtIndex(offset) as ComponentInstance
|
226
222
|
const sharedComponent = this.createSharedComponentByComponent(component)
|
227
223
|
content.insertEmbed(offset, sharedComponent)
|
228
224
|
}
|
229
|
-
|
230
|
-
content.format(offset, length, action.formats)
|
231
|
-
}
|
225
|
+
|
232
226
|
if (isEmpty && offset === 0) {
|
233
227
|
content.delete(content.length - 1, 1)
|
234
228
|
}
|
@@ -243,6 +237,12 @@ export class Collaborate implements History {
|
|
243
237
|
}
|
244
238
|
})
|
245
239
|
})
|
240
|
+
|
241
|
+
sub.add(slot.onChildComponentRemove.subscribe(components => {
|
242
|
+
components.forEach(c => {
|
243
|
+
this.cleanSubscriptionsByComponent(c)
|
244
|
+
})
|
245
|
+
}))
|
246
246
|
this.contentSyncCaches.set(slot, () => {
|
247
247
|
content.unobserve(syncRemote)
|
248
248
|
sub.unsubscribe()
|
@@ -281,18 +281,21 @@ export class Collaborate implements History {
|
|
281
281
|
const slots = component.slots
|
282
282
|
const syncRemote = (ev, tr) => {
|
283
283
|
this.runRemoteUpdate(tr, () => {
|
284
|
+
let index = 0
|
284
285
|
ev.delta.forEach(action => {
|
285
286
|
if (Reflect.has(action, 'retain')) {
|
286
|
-
|
287
|
+
index += action.retain
|
288
|
+
slots.retain(index)
|
287
289
|
} else if (action.insert) {
|
288
290
|
(action.insert as Array<YMap<any>>).forEach(item => {
|
289
291
|
const slot = this.createSlotBySharedSlot(item)
|
290
292
|
slots.insert(slot)
|
291
293
|
this.syncContent(item.get('content'), slot)
|
292
294
|
this.syncSlot(item, slot)
|
295
|
+
index++
|
293
296
|
})
|
294
297
|
} else if (action.delete) {
|
295
|
-
slots.retain(
|
298
|
+
slots.retain(index)
|
296
299
|
slots.delete(action.delete)
|
297
300
|
}
|
298
301
|
})
|
@@ -313,15 +316,18 @@ export class Collaborate implements History {
|
|
313
316
|
remoteSlots.insert(index, [sharedSlot])
|
314
317
|
index++
|
315
318
|
} else if (action.type === 'delete') {
|
316
|
-
slots.slice(index, index + action.count).forEach(slot => {
|
317
|
-
this.cleanSubscriptionsBySlot(slot)
|
318
|
-
})
|
319
319
|
remoteSlots.delete(index, action.count)
|
320
320
|
}
|
321
321
|
})
|
322
322
|
})
|
323
323
|
})
|
324
324
|
|
325
|
+
sub.add(slots.onChildSlotRemove.subscribe(slots => {
|
326
|
+
slots.forEach(slot => {
|
327
|
+
this.cleanSubscriptionsBySlot(slot)
|
328
|
+
})
|
329
|
+
}))
|
330
|
+
|
325
331
|
this.slotsSyncCaches.set(component, () => {
|
326
332
|
remoteSlots.unobserve(syncRemote)
|
327
333
|
sub.unsubscribe()
|
@@ -366,7 +372,7 @@ export class Collaborate implements History {
|
|
366
372
|
return
|
367
373
|
}
|
368
374
|
this.updateFromRemote = true
|
369
|
-
fn
|
375
|
+
this.scheduler.remoteUpdateTransact(fn)
|
370
376
|
this.updateFromRemote = false
|
371
377
|
}
|
372
378
|
|
@@ -414,7 +420,7 @@ export class Collaborate implements History {
|
|
414
420
|
return sharedSlot
|
415
421
|
}
|
416
422
|
|
417
|
-
private createComponentBySharedComponent(yMap: YMap<any
|
423
|
+
private createComponentBySharedComponent(yMap: YMap<any>, canInsertInlineComponent: boolean): ComponentInstance {
|
418
424
|
const sharedSlots = yMap.get('slots') as YArray<YMap<any>>
|
419
425
|
const slots: Slot[] = []
|
420
426
|
sharedSlots.forEach(sharedSlot => {
|
@@ -428,13 +434,17 @@ export class Collaborate implements History {
|
|
428
434
|
})
|
429
435
|
if (instance) {
|
430
436
|
instance.slots.toArray().forEach((slot, index) => {
|
431
|
-
|
437
|
+
let sharedSlot = sharedSlots.get(index)
|
438
|
+
if (!sharedSlot) {
|
439
|
+
sharedSlot = this.createSharedSlotBySlot(slot)
|
440
|
+
sharedSlots.push([sharedSlot])
|
441
|
+
}
|
432
442
|
this.syncSlot(sharedSlot, slot)
|
433
443
|
this.syncContent(sharedSlot.get('content'), slot)
|
434
444
|
})
|
435
445
|
return instance
|
436
446
|
}
|
437
|
-
|
447
|
+
return createUnknownComponent(name, canInsertInlineComponent).createInstance(this.starter)
|
438
448
|
}
|
439
449
|
|
440
450
|
private createSlotBySharedSlot(sharedSlot: YMap<any>): Slot {
|
@@ -454,7 +464,8 @@ export class Collaborate implements History {
|
|
454
464
|
slot.insert(action.insert, makeFormats(this.registry, action.attributes))
|
455
465
|
} else {
|
456
466
|
const sharedComponent = action.insert as YMap<any>
|
457
|
-
const
|
467
|
+
const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent)
|
468
|
+
const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent)
|
458
469
|
slot.insert(component)
|
459
470
|
this.syncSlots(sharedComponent.get('slots'), component)
|
460
471
|
this.syncComponent(sharedComponent, component)
|
@@ -494,7 +505,7 @@ export class Collaborate implements History {
|
|
494
505
|
function makeFormats(registry: Registry, attrs?: any) {
|
495
506
|
const formats: Formats = []
|
496
507
|
if (attrs) {
|
497
|
-
Object.keys(attrs).
|
508
|
+
Object.keys(attrs).forEach(key => {
|
498
509
|
const formatter = registry.getFormatter(key)
|
499
510
|
if (formatter) {
|
500
511
|
formats.push([formatter, attrs[key]])
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { Plugin, Renderer, Scheduler } from '@textbus/core'
|
2
|
+
import { Injector } from '@tanbo/di'
|
3
|
+
import { Caret, CaretPosition } from '@textbus/browser'
|
4
|
+
import { Subscription } from '@tanbo/stream'
|
5
|
+
|
6
|
+
export class FixedCaretPlugin implements Plugin {
|
7
|
+
private subscriptions = new Subscription()
|
8
|
+
|
9
|
+
constructor(public scrollContainer: HTMLElement) {
|
10
|
+
}
|
11
|
+
|
12
|
+
setup(injector: Injector) {
|
13
|
+
const scheduler = injector.get(Scheduler)
|
14
|
+
const caret = injector.get(Caret)
|
15
|
+
const renderer = injector.get(Renderer)
|
16
|
+
|
17
|
+
let isChanged = false
|
18
|
+
let caretPosition: CaretPosition | null = null
|
19
|
+
renderer.onViewChecked.subscribe(() => {
|
20
|
+
isChanged = true
|
21
|
+
})
|
22
|
+
this.subscriptions.add(caret.onPositionChange.subscribe(position => {
|
23
|
+
if (isChanged && caretPosition && position && !scheduler.hasLocalUpdate) {
|
24
|
+
const offset = position.top - caretPosition.top
|
25
|
+
this.scrollContainer.scrollTop += offset
|
26
|
+
isChanged = false
|
27
|
+
}
|
28
|
+
caretPosition = position
|
29
|
+
}))
|
30
|
+
}
|
31
|
+
|
32
|
+
onDestroy() {
|
33
|
+
this.subscriptions.unsubscribe()
|
34
|
+
}
|
35
|
+
}
|
package/src/public-api.ts
CHANGED
@@ -1,2 +1,19 @@
|
|
1
|
-
|
1
|
+
import { History, Module } from '@textbus/core'
|
2
|
+
|
3
|
+
import { Collaborate } from './collaborate'
|
4
|
+
import { CollaborateCursor } from './collaborate-cursor'
|
5
|
+
|
2
6
|
export * from './collaborate'
|
7
|
+
export * from './collaborate-cursor'
|
8
|
+
export * from './fixed-caret.plugin'
|
9
|
+
|
10
|
+
export const collaborateModule: Module = {
|
11
|
+
providers: [
|
12
|
+
Collaborate,
|
13
|
+
CollaborateCursor,
|
14
|
+
{
|
15
|
+
provide: History,
|
16
|
+
useClass: Collaborate
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { ContentType, defineComponent, VElement } from '@textbus/core'
|
2
|
+
|
3
|
+
export function createUnknownComponent(factoryName: string, canInsertInlineComponent: boolean) {
|
4
|
+
const unknownComponent = defineComponent({
|
5
|
+
type: canInsertInlineComponent ? ContentType.InlineComponent : ContentType.BlockComponent,
|
6
|
+
name: 'UnknownComponent',
|
7
|
+
setup() {
|
8
|
+
console.error(`cannot find component factory \`${factoryName}\`.`)
|
9
|
+
return {
|
10
|
+
render() {
|
11
|
+
return VElement.createElement('textbus-unknown-component', {
|
12
|
+
style: {
|
13
|
+
display: canInsertInlineComponent ? 'inline' : 'block',
|
14
|
+
color: '#f00'
|
15
|
+
}
|
16
|
+
}, unknownComponent.name)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
})
|
21
|
+
return unknownComponent
|
22
|
+
}
|
package/bundles/collab/_api.d.ts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export * from './collaborate-cursor';
|
package/bundles/collab/_api.js
DELETED
@@ -1,2 +0,0 @@
|
|
1
|
-
export * from './collaborate-cursor';
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiX2FwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb2xsYWIvX2FwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHNCQUFzQixDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9jb2xsYWJvcmF0ZS1jdXJzb3InXG4iXX0=
|