@textbus/collaborate 2.2.0 → 2.3.0
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/package.json +4 -4
- package/src/collaborate-cursor.ts +0 -293
- package/src/collaborate.ts +0 -732
- package/src/public-api.ts +0 -18
- package/src/unknown.component.ts +0 -22
- package/tsconfig-build.json +0 -28
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@textbus/collaborate",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.3.0",
|
4
4
|
"description": "Textbus is a rich text editor and framework that is highly customizable and extensible to achieve rich wysiwyg effects.",
|
5
5
|
"main": "./bundles/public-api.js",
|
6
6
|
"module": "./bundles/public-api.js",
|
@@ -27,8 +27,8 @@
|
|
27
27
|
"dependencies": {
|
28
28
|
"@tanbo/di": "^1.1.3",
|
29
29
|
"@tanbo/stream": "^1.1.8",
|
30
|
-
"@textbus/browser": "^2.
|
31
|
-
"@textbus/core": "^2.
|
30
|
+
"@textbus/browser": "^2.3.0",
|
31
|
+
"@textbus/core": "^2.3.0",
|
32
32
|
"reflect-metadata": "^0.1.13",
|
33
33
|
"y-protocols": "^1.0.5",
|
34
34
|
"yjs": "^13.5.39"
|
@@ -44,5 +44,5 @@
|
|
44
44
|
"bugs": {
|
45
45
|
"url": "https://github.com/textbus/textbus.git/issues"
|
46
46
|
},
|
47
|
-
"gitHead": "
|
47
|
+
"gitHead": "11eaf834d94ebf5b4392837ba0538f6366b38c4a"
|
48
48
|
}
|
@@ -1,293 +0,0 @@
|
|
1
|
-
import { Inject, Injectable, Optional } from '@tanbo/di'
|
2
|
-
import {
|
3
|
-
createElement,
|
4
|
-
VIEW_CONTAINER,
|
5
|
-
getLayoutRectByRange,
|
6
|
-
SelectionBridge
|
7
|
-
} from '@textbus/browser'
|
8
|
-
import { Selection, SelectionPaths, AbstractSelection, Scheduler, Rect } from '@textbus/core'
|
9
|
-
import { fromEvent, Subject, Subscription } from '@tanbo/stream'
|
10
|
-
|
11
|
-
export interface RemoteSelection {
|
12
|
-
id: string
|
13
|
-
color: string
|
14
|
-
username: string
|
15
|
-
paths: SelectionPaths
|
16
|
-
}
|
17
|
-
|
18
|
-
export interface SelectionRect extends Rect {
|
19
|
-
color: string
|
20
|
-
username: string
|
21
|
-
id: string
|
22
|
-
}
|
23
|
-
|
24
|
-
export interface RemoteSelectionCursor {
|
25
|
-
cursor: HTMLElement
|
26
|
-
anchor: HTMLElement
|
27
|
-
userTip: HTMLElement
|
28
|
-
}
|
29
|
-
|
30
|
-
export abstract class CollaborateSelectionAwarenessDelegate {
|
31
|
-
abstract getRects(abstractSelection: AbstractSelection, nativeRange: Range): false | Rect[]
|
32
|
-
}
|
33
|
-
|
34
|
-
@Injectable()
|
35
|
-
export class CollaborateCursor {
|
36
|
-
private host = createElement('div', {
|
37
|
-
styles: {
|
38
|
-
position: 'absolute',
|
39
|
-
left: 0,
|
40
|
-
top: 0,
|
41
|
-
width: '100%',
|
42
|
-
height: '100%',
|
43
|
-
pointerEvents: 'none',
|
44
|
-
zIndex: 1
|
45
|
-
}
|
46
|
-
})
|
47
|
-
private canvasContainer = createElement('div', {
|
48
|
-
styles: {
|
49
|
-
position: 'absolute',
|
50
|
-
left: 0,
|
51
|
-
top: 0,
|
52
|
-
width: '100%',
|
53
|
-
height: '100%',
|
54
|
-
overflow: 'hidden'
|
55
|
-
}
|
56
|
-
})
|
57
|
-
private canvas = createElement('canvas', {
|
58
|
-
styles: {
|
59
|
-
position: 'absolute',
|
60
|
-
opacity: 0.5,
|
61
|
-
left: 0,
|
62
|
-
top: 0,
|
63
|
-
width: '100%',
|
64
|
-
height: document.documentElement.clientHeight + 'px',
|
65
|
-
pointerEvents: 'none',
|
66
|
-
}
|
67
|
-
}) as HTMLCanvasElement
|
68
|
-
private context = this.canvas.getContext('2d')!
|
69
|
-
private tooltips = createElement('div', {
|
70
|
-
styles: {
|
71
|
-
position: 'absolute',
|
72
|
-
left: 0,
|
73
|
-
top: 0,
|
74
|
-
width: '100%',
|
75
|
-
height: '100%',
|
76
|
-
pointerEvents: 'none',
|
77
|
-
fontSize: '12px',
|
78
|
-
zIndex: 10
|
79
|
-
}
|
80
|
-
})
|
81
|
-
|
82
|
-
private onRectsChange = new Subject<SelectionRect[]>()
|
83
|
-
|
84
|
-
private subscription = new Subscription()
|
85
|
-
private currentSelection: RemoteSelection[] = []
|
86
|
-
|
87
|
-
constructor(@Inject(VIEW_CONTAINER) private container: HTMLElement,
|
88
|
-
@Optional() private awarenessDelegate: CollaborateSelectionAwarenessDelegate,
|
89
|
-
private nativeSelection: SelectionBridge,
|
90
|
-
private scheduler: Scheduler,
|
91
|
-
private selection: Selection) {
|
92
|
-
this.canvasContainer.append(this.canvas)
|
93
|
-
this.host.append(this.canvasContainer, this.tooltips)
|
94
|
-
container.prepend(this.host)
|
95
|
-
this.subscription.add(this.onRectsChange.subscribe(rects => {
|
96
|
-
for (const rect of rects) {
|
97
|
-
this.context.fillStyle = rect.color
|
98
|
-
this.context.beginPath()
|
99
|
-
this.context.rect(rect.left, rect.top, rect.width, rect.height)
|
100
|
-
this.context.fill()
|
101
|
-
this.context.closePath()
|
102
|
-
}
|
103
|
-
}), fromEvent(window, 'resize').subscribe(() => {
|
104
|
-
this.canvas.style.height = document.documentElement.clientHeight + 'px'
|
105
|
-
this.refresh()
|
106
|
-
}), this.scheduler.onDocChanged.subscribe(() => {
|
107
|
-
this.refresh()
|
108
|
-
}))
|
109
|
-
}
|
110
|
-
|
111
|
-
refresh() {
|
112
|
-
this.draw(this.currentSelection)
|
113
|
-
}
|
114
|
-
|
115
|
-
destroy() {
|
116
|
-
this.subscription.unsubscribe()
|
117
|
-
}
|
118
|
-
|
119
|
-
draw(paths: RemoteSelection[]) {
|
120
|
-
this.currentSelection = paths
|
121
|
-
const containerRect = this.container.getBoundingClientRect()
|
122
|
-
this.canvas.style.top = containerRect.top * -1 + 'px'
|
123
|
-
this.canvas.width = this.canvas.offsetWidth
|
124
|
-
this.canvas.height = this.canvas.offsetHeight
|
125
|
-
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
126
|
-
|
127
|
-
const users: SelectionRect[] = []
|
128
|
-
|
129
|
-
paths.filter(i => {
|
130
|
-
return i.paths.anchor.length && i.paths.focus.length
|
131
|
-
}).forEach(item => {
|
132
|
-
const anchorPaths = [...item.paths.anchor]
|
133
|
-
const focusPaths = [...item.paths.focus]
|
134
|
-
const anchorOffset = anchorPaths.pop()!
|
135
|
-
const anchorSlot = this.selection.findSlotByPaths(anchorPaths)
|
136
|
-
const focusOffset = focusPaths.pop()!
|
137
|
-
const focusSlot = this.selection.findSlotByPaths(focusPaths)
|
138
|
-
if (!anchorSlot || !focusSlot) {
|
139
|
-
return
|
140
|
-
}
|
141
|
-
|
142
|
-
const { focus, anchor } = this.nativeSelection.getPositionByRange({
|
143
|
-
focusOffset,
|
144
|
-
anchorOffset,
|
145
|
-
focusSlot,
|
146
|
-
anchorSlot
|
147
|
-
})
|
148
|
-
if (!focus || !anchor) {
|
149
|
-
return
|
150
|
-
}
|
151
|
-
const nativeRange = document.createRange()
|
152
|
-
nativeRange.setStart(anchor.node, anchor.offset)
|
153
|
-
nativeRange.setEnd(focus.node, focus.offset)
|
154
|
-
if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
|
155
|
-
nativeRange.setStart(focus.node, focus.offset)
|
156
|
-
nativeRange.setEnd(anchor.node, anchor.offset)
|
157
|
-
}
|
158
|
-
|
159
|
-
let rects: Rect[] | DOMRectList | false = false
|
160
|
-
if (this.awarenessDelegate) {
|
161
|
-
rects = this.awarenessDelegate.getRects({
|
162
|
-
focusOffset,
|
163
|
-
anchorOffset,
|
164
|
-
focusSlot,
|
165
|
-
anchorSlot
|
166
|
-
}, nativeRange)
|
167
|
-
}
|
168
|
-
if (!rects) {
|
169
|
-
rects = nativeRange.getClientRects()
|
170
|
-
}
|
171
|
-
const selectionRects: SelectionRect[] = []
|
172
|
-
for (let i = rects.length - 1; i >= 0; i--) {
|
173
|
-
const rect = rects[i]
|
174
|
-
selectionRects.push({
|
175
|
-
id: item.id,
|
176
|
-
color: item.color,
|
177
|
-
username: item.username,
|
178
|
-
left: rect.left - containerRect.left,
|
179
|
-
top: rect.top,
|
180
|
-
width: rect.width,
|
181
|
-
height: rect.height,
|
182
|
-
})
|
183
|
-
}
|
184
|
-
this.onRectsChange.next(selectionRects)
|
185
|
-
|
186
|
-
const cursorRange = nativeRange.cloneRange()
|
187
|
-
cursorRange.setStart(focus.node, focus.offset)
|
188
|
-
cursorRange.collapse(true)
|
189
|
-
|
190
|
-
const cursorRect = getLayoutRectByRange(cursorRange)
|
191
|
-
|
192
|
-
const rect: SelectionRect = {
|
193
|
-
id: item.id,
|
194
|
-
username: item.username,
|
195
|
-
color: item.color,
|
196
|
-
left: cursorRect.left - containerRect.left,
|
197
|
-
top: cursorRect.top - containerRect.top,
|
198
|
-
width: 2,
|
199
|
-
height: cursorRect.height
|
200
|
-
}
|
201
|
-
if (rect.left < 0 || rect.top < 0 || rect.left > containerRect.width) {
|
202
|
-
return
|
203
|
-
}
|
204
|
-
users.push(rect)
|
205
|
-
})
|
206
|
-
this.drawUserCursor(users)
|
207
|
-
}
|
208
|
-
|
209
|
-
protected drawUserCursor(rects: SelectionRect[]) {
|
210
|
-
for (let i = 0; i < rects.length; i++) {
|
211
|
-
const rect = rects[i]
|
212
|
-
const { cursor, userTip, anchor } = this.getUserCursor(i)
|
213
|
-
Object.assign(cursor.style, {
|
214
|
-
left: rect.left + 'px',
|
215
|
-
top: rect.top + 'px',
|
216
|
-
width: rect.width + 'px',
|
217
|
-
height: rect.height + 'px',
|
218
|
-
background: rect.color,
|
219
|
-
display: 'block'
|
220
|
-
})
|
221
|
-
anchor.style.background = rect.color
|
222
|
-
userTip.innerText = rect.username
|
223
|
-
userTip.style.background = rect.color
|
224
|
-
}
|
225
|
-
|
226
|
-
for (let i = rects.length; i < this.tooltips.children.length; i++) {
|
227
|
-
this.tooltips.removeChild(this.tooltips.children[i])
|
228
|
-
}
|
229
|
-
}
|
230
|
-
|
231
|
-
private getUserCursor(index: number): RemoteSelectionCursor {
|
232
|
-
let child: HTMLElement = this.tooltips.children[index] as HTMLElement
|
233
|
-
if (child) {
|
234
|
-
const anchor = child.children[0] as HTMLElement
|
235
|
-
return {
|
236
|
-
cursor: child,
|
237
|
-
anchor,
|
238
|
-
userTip: anchor.children[0] as HTMLElement
|
239
|
-
}
|
240
|
-
}
|
241
|
-
const userTip = createElement('span', {
|
242
|
-
styles: {
|
243
|
-
position: 'absolute',
|
244
|
-
display: 'none',
|
245
|
-
left: '50%',
|
246
|
-
transform: 'translateX(-50%)',
|
247
|
-
marginBottom: '2px',
|
248
|
-
bottom: '100%',
|
249
|
-
whiteSpace: 'nowrap',
|
250
|
-
color: '#fff',
|
251
|
-
boxShadow: '0 1px 2px rgba(0,0,0,.1)',
|
252
|
-
borderRadius: '3px',
|
253
|
-
padding: '3px 5px',
|
254
|
-
pointerEvents: 'none',
|
255
|
-
}
|
256
|
-
})
|
257
|
-
|
258
|
-
const anchor = createElement('span', {
|
259
|
-
styles: {
|
260
|
-
position: 'absolute',
|
261
|
-
top: '-2px',
|
262
|
-
left: '-2px',
|
263
|
-
width: '6px',
|
264
|
-
height: '6px',
|
265
|
-
pointerEvents: 'auto',
|
266
|
-
pointer: 'cursor',
|
267
|
-
},
|
268
|
-
children: [userTip],
|
269
|
-
on: {
|
270
|
-
mouseenter() {
|
271
|
-
userTip.style.display = 'block'
|
272
|
-
},
|
273
|
-
mouseleave() {
|
274
|
-
userTip.style.display = 'none'
|
275
|
-
}
|
276
|
-
}
|
277
|
-
})
|
278
|
-
child = createElement('span', {
|
279
|
-
styles: {
|
280
|
-
position: 'absolute',
|
281
|
-
},
|
282
|
-
children: [
|
283
|
-
anchor
|
284
|
-
]
|
285
|
-
})
|
286
|
-
this.tooltips.append(child)
|
287
|
-
return {
|
288
|
-
cursor: child,
|
289
|
-
anchor,
|
290
|
-
userTip
|
291
|
-
}
|
292
|
-
}
|
293
|
-
}
|
package/src/collaborate.ts
DELETED
@@ -1,732 +0,0 @@
|
|
1
|
-
import { Inject, Injectable } from '@tanbo/di'
|
2
|
-
import { delay, filter, map, Observable, Subject, Subscription } from '@tanbo/stream'
|
3
|
-
import {
|
4
|
-
ChangeOrigin,
|
5
|
-
ComponentInstance,
|
6
|
-
ContentType,
|
7
|
-
Controller,
|
8
|
-
Formats,
|
9
|
-
History,
|
10
|
-
HISTORY_STACK_SIZE,
|
11
|
-
makeError,
|
12
|
-
Registry,
|
13
|
-
RootComponentRef,
|
14
|
-
Scheduler,
|
15
|
-
Selection,
|
16
|
-
SelectionPaths,
|
17
|
-
Slot,
|
18
|
-
Starter,
|
19
|
-
Translator
|
20
|
-
} from '@textbus/core'
|
21
|
-
import {
|
22
|
-
Array as YArray,
|
23
|
-
Doc as YDoc,
|
24
|
-
Map as YMap,
|
25
|
-
RelativePosition,
|
26
|
-
Text as YText,
|
27
|
-
Transaction,
|
28
|
-
UndoManager,
|
29
|
-
createAbsolutePositionFromRelativePosition,
|
30
|
-
createRelativePositionFromTypeIndex
|
31
|
-
} from 'yjs'
|
32
|
-
|
33
|
-
import { CollaborateCursor, RemoteSelection } from './collaborate-cursor'
|
34
|
-
import { createUnknownComponent } from './unknown.component'
|
35
|
-
|
36
|
-
const collaborateErrorFn = makeError('Collaborate')
|
37
|
-
|
38
|
-
interface CursorPosition {
|
39
|
-
anchor: RelativePosition
|
40
|
-
focus: RelativePosition
|
41
|
-
}
|
42
|
-
|
43
|
-
class ContentMap {
|
44
|
-
private slotAndYTextMap = new WeakMap<Slot, YText>()
|
45
|
-
private yTextAndSLotMap = new WeakMap<YText, Slot>()
|
46
|
-
|
47
|
-
set(key: Slot, value: YText): void
|
48
|
-
set(key: YText, value: Slot): void
|
49
|
-
set(key: any, value: any) {
|
50
|
-
if (key instanceof Slot) {
|
51
|
-
this.slotAndYTextMap.set(key, value)
|
52
|
-
this.yTextAndSLotMap.set(value, key)
|
53
|
-
} else {
|
54
|
-
this.slotAndYTextMap.set(value, key)
|
55
|
-
this.yTextAndSLotMap.set(key, value)
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
|
-
get(key: Slot): YText | null
|
60
|
-
get(key: YText): Slot | null
|
61
|
-
get(key: any) {
|
62
|
-
if (key instanceof Slot) {
|
63
|
-
return this.slotAndYTextMap.get(key) || null
|
64
|
-
}
|
65
|
-
return this.yTextAndSLotMap.get(key) || null
|
66
|
-
}
|
67
|
-
|
68
|
-
delete(key: Slot | YText) {
|
69
|
-
if (key instanceof Slot) {
|
70
|
-
const v = this.slotAndYTextMap.get(key)
|
71
|
-
this.slotAndYTextMap.delete(key)
|
72
|
-
if (v) {
|
73
|
-
this.yTextAndSLotMap.delete(v)
|
74
|
-
}
|
75
|
-
} else {
|
76
|
-
const v = this.yTextAndSLotMap.get(key)
|
77
|
-
this.yTextAndSLotMap.delete(key)
|
78
|
-
if (v) {
|
79
|
-
this.slotAndYTextMap.delete(v)
|
80
|
-
}
|
81
|
-
}
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
interface Update {
|
86
|
-
record: boolean
|
87
|
-
actions: Array<() => void>
|
88
|
-
}
|
89
|
-
|
90
|
-
interface UpdateItem {
|
91
|
-
record: boolean
|
92
|
-
|
93
|
-
action(): void
|
94
|
-
}
|
95
|
-
|
96
|
-
@Injectable()
|
97
|
-
export class Collaborate implements History {
|
98
|
-
onSelectionChange: Observable<SelectionPaths>
|
99
|
-
yDoc = new YDoc()
|
100
|
-
onBack: Observable<void>
|
101
|
-
onForward: Observable<void>
|
102
|
-
onChange: Observable<void>
|
103
|
-
onPush: Observable<void>
|
104
|
-
|
105
|
-
get canBack() {
|
106
|
-
return this.manager?.canUndo() || false
|
107
|
-
}
|
108
|
-
|
109
|
-
get canForward() {
|
110
|
-
return this.manager?.canRedo() || false
|
111
|
-
}
|
112
|
-
|
113
|
-
protected backEvent = new Subject<void>()
|
114
|
-
protected forwardEvent = new Subject<void>()
|
115
|
-
protected changeEvent = new Subject<void>()
|
116
|
-
protected pushEvent = new Subject<void>()
|
117
|
-
|
118
|
-
protected manager: UndoManager | null = null
|
119
|
-
|
120
|
-
protected subscriptions: Subscription[] = []
|
121
|
-
protected updateFromRemote = false
|
122
|
-
|
123
|
-
protected contentSyncCaches = new WeakMap<Slot, () => void>()
|
124
|
-
protected slotStateSyncCaches = new WeakMap<Slot, () => void>()
|
125
|
-
protected slotsSyncCaches = new WeakMap<ComponentInstance, () => void>()
|
126
|
-
protected componentStateSyncCaches = new WeakMap<ComponentInstance, () => void>()
|
127
|
-
|
128
|
-
protected selectionChangeEvent = new Subject<SelectionPaths>()
|
129
|
-
protected contentMap = new ContentMap()
|
130
|
-
|
131
|
-
protected updateRemoteActions: Array<UpdateItem> = []
|
132
|
-
protected noRecord = {}
|
133
|
-
|
134
|
-
constructor(@Inject(HISTORY_STACK_SIZE) protected stackSize: number,
|
135
|
-
protected rootComponentRef: RootComponentRef,
|
136
|
-
protected collaborateCursor: CollaborateCursor,
|
137
|
-
protected controller: Controller,
|
138
|
-
protected scheduler: Scheduler,
|
139
|
-
protected translator: Translator,
|
140
|
-
protected registry: Registry,
|
141
|
-
protected selection: Selection,
|
142
|
-
protected starter: Starter) {
|
143
|
-
this.onSelectionChange = this.selectionChangeEvent.asObservable().pipe(delay())
|
144
|
-
this.onBack = this.backEvent.asObservable()
|
145
|
-
this.onForward = this.forwardEvent.asObservable()
|
146
|
-
this.onChange = this.changeEvent.asObservable()
|
147
|
-
this.onPush = this.pushEvent.asObservable()
|
148
|
-
}
|
149
|
-
|
150
|
-
listen() {
|
151
|
-
const root = this.yDoc.getMap('RootComponent')
|
152
|
-
const rootComponent = this.rootComponentRef.component!
|
153
|
-
this.manager = new UndoManager(root, {
|
154
|
-
trackedOrigins: new Set<any>([this.yDoc])
|
155
|
-
})
|
156
|
-
const cursorKey = 'cursor-position'
|
157
|
-
this.manager.on('stack-item-added', event => {
|
158
|
-
event.stackItem.meta.set(cursorKey, this.getRelativeCursorLocation())
|
159
|
-
if (this.manager!.undoStack.length > this.stackSize) {
|
160
|
-
this.manager!.undoStack.shift()
|
161
|
-
}
|
162
|
-
if (event.origin === this.yDoc) {
|
163
|
-
this.pushEvent.next()
|
164
|
-
}
|
165
|
-
this.changeEvent.next()
|
166
|
-
})
|
167
|
-
this.manager.on('stack-item-popped', event => {
|
168
|
-
const position = event.stackItem.meta.get(cursorKey) as CursorPosition
|
169
|
-
if (position) {
|
170
|
-
this.restoreCursorLocation(position)
|
171
|
-
}
|
172
|
-
})
|
173
|
-
this.subscriptions.push(
|
174
|
-
this.selection.onChange.subscribe(() => {
|
175
|
-
const paths = this.selection.getPaths()
|
176
|
-
this.selectionChangeEvent.next(paths)
|
177
|
-
}),
|
178
|
-
this.scheduler.onDocChanged.pipe(
|
179
|
-
map(item => {
|
180
|
-
return item.filter(i => {
|
181
|
-
return i.from !== ChangeOrigin.Remote
|
182
|
-
})
|
183
|
-
}),
|
184
|
-
filter(item => {
|
185
|
-
return item.length
|
186
|
-
})
|
187
|
-
).subscribe(() => {
|
188
|
-
const updates: Update[] = []
|
189
|
-
|
190
|
-
let update: Update | null = null
|
191
|
-
|
192
|
-
for (const item of this.updateRemoteActions) {
|
193
|
-
if (!update) {
|
194
|
-
update = {
|
195
|
-
record: item.record,
|
196
|
-
actions: []
|
197
|
-
}
|
198
|
-
updates.push(update)
|
199
|
-
}
|
200
|
-
if (update.record === item.record) {
|
201
|
-
update.actions.push(item.action)
|
202
|
-
} else {
|
203
|
-
update = {
|
204
|
-
record: item.record,
|
205
|
-
actions: [item.action]
|
206
|
-
}
|
207
|
-
updates.push(update)
|
208
|
-
}
|
209
|
-
}
|
210
|
-
|
211
|
-
this.updateRemoteActions = []
|
212
|
-
|
213
|
-
for (const item of updates) {
|
214
|
-
this.yDoc.transact(() => {
|
215
|
-
item.actions.forEach(fn => {
|
216
|
-
fn()
|
217
|
-
})
|
218
|
-
}, item.record ? this.yDoc : this.noRecord)
|
219
|
-
}
|
220
|
-
})
|
221
|
-
)
|
222
|
-
this.syncRootComponent(root, rootComponent)
|
223
|
-
}
|
224
|
-
|
225
|
-
updateRemoteSelection(paths: RemoteSelection[]) {
|
226
|
-
this.collaborateCursor.draw(paths)
|
227
|
-
}
|
228
|
-
|
229
|
-
back() {
|
230
|
-
if (this.canBack) {
|
231
|
-
this.manager?.undo()
|
232
|
-
this.backEvent.next()
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
236
|
-
forward() {
|
237
|
-
if (this.canForward) {
|
238
|
-
this.manager?.redo()
|
239
|
-
this.forwardEvent.next()
|
240
|
-
}
|
241
|
-
}
|
242
|
-
|
243
|
-
clear() {
|
244
|
-
this.manager?.clear()
|
245
|
-
this.changeEvent.next()
|
246
|
-
}
|
247
|
-
|
248
|
-
destroy() {
|
249
|
-
this.subscriptions.forEach(i => i.unsubscribe())
|
250
|
-
this.collaborateCursor.destroy()
|
251
|
-
this.manager?.destroy()
|
252
|
-
}
|
253
|
-
|
254
|
-
protected syncRootComponent(root: YMap<any>, rootComponent: ComponentInstance) {
|
255
|
-
let slots = root.get('slots') as YArray<YMap<any>>
|
256
|
-
if (!slots) {
|
257
|
-
slots = new YArray()
|
258
|
-
rootComponent.slots.toArray().forEach(i => {
|
259
|
-
const sharedSlot = this.createSharedSlotBySlot(i)
|
260
|
-
slots.push([sharedSlot])
|
261
|
-
})
|
262
|
-
this.yDoc.transact(() => {
|
263
|
-
root.set('state', rootComponent.state)
|
264
|
-
root.set('slots', slots)
|
265
|
-
})
|
266
|
-
} else if (slots.length === 0) {
|
267
|
-
rootComponent.updateState(() => {
|
268
|
-
return root.get('state')
|
269
|
-
})
|
270
|
-
this.yDoc.transact(() => {
|
271
|
-
rootComponent.slots.toArray().forEach(i => {
|
272
|
-
const sharedSlot = this.createSharedSlotBySlot(i)
|
273
|
-
slots.push([sharedSlot])
|
274
|
-
})
|
275
|
-
})
|
276
|
-
} else {
|
277
|
-
rootComponent.updateState(() => {
|
278
|
-
return root.get('state')
|
279
|
-
})
|
280
|
-
rootComponent.slots.clean()
|
281
|
-
slots.forEach(sharedSlot => {
|
282
|
-
const slot = this.createSlotBySharedSlot(sharedSlot)
|
283
|
-
this.syncContent(sharedSlot.get('content'), slot)
|
284
|
-
this.syncSlot(sharedSlot, slot)
|
285
|
-
rootComponent.slots.insert(slot)
|
286
|
-
})
|
287
|
-
}
|
288
|
-
this.syncComponent(root, rootComponent)
|
289
|
-
this.syncSlots(slots, rootComponent)
|
290
|
-
}
|
291
|
-
|
292
|
-
protected restoreCursorLocation(position: CursorPosition) {
|
293
|
-
const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc)
|
294
|
-
const focusPosition = createAbsolutePositionFromRelativePosition(position.focus, this.yDoc)
|
295
|
-
if (anchorPosition && focusPosition) {
|
296
|
-
const focusSlot = this.contentMap.get(focusPosition.type as YText)
|
297
|
-
const anchorSlot = this.contentMap.get(anchorPosition.type as YText)
|
298
|
-
if (focusSlot && anchorSlot) {
|
299
|
-
this.selection.setBaseAndExtent(anchorSlot, anchorPosition.index, focusSlot, focusPosition.index)
|
300
|
-
return
|
301
|
-
}
|
302
|
-
}
|
303
|
-
this.selection.unSelect()
|
304
|
-
}
|
305
|
-
|
306
|
-
protected getRelativeCursorLocation(): CursorPosition | null {
|
307
|
-
const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection
|
308
|
-
if (anchorSlot) {
|
309
|
-
const anchorYText = this.contentMap.get(anchorSlot)
|
310
|
-
if (anchorYText) {
|
311
|
-
const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset!)
|
312
|
-
if (focusSlot) {
|
313
|
-
const focusYText = this.contentMap.get(focusSlot)
|
314
|
-
if (focusYText) {
|
315
|
-
const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset!)
|
316
|
-
return {
|
317
|
-
focus: focusPosition,
|
318
|
-
anchor: anchorPosition
|
319
|
-
}
|
320
|
-
}
|
321
|
-
}
|
322
|
-
}
|
323
|
-
}
|
324
|
-
return null
|
325
|
-
}
|
326
|
-
|
327
|
-
protected syncContent(content: YText, slot: Slot) {
|
328
|
-
this.contentMap.set(slot, content)
|
329
|
-
const syncRemote = (ev, tr) => {
|
330
|
-
this.runRemoteUpdate(tr, () => {
|
331
|
-
slot.retain(0)
|
332
|
-
ev.delta.forEach(action => {
|
333
|
-
if (Reflect.has(action, 'retain')) {
|
334
|
-
if (action.attributes) {
|
335
|
-
const formats = remoteFormatsToLocal(this.registry, action.attributes)
|
336
|
-
if (formats.length) {
|
337
|
-
slot.retain(action.retain!, formats)
|
338
|
-
}
|
339
|
-
slot.retain(slot.index + action.retain)
|
340
|
-
} else {
|
341
|
-
slot.retain(action.retain)
|
342
|
-
}
|
343
|
-
} else if (action.insert) {
|
344
|
-
const index = slot.index
|
345
|
-
let length = 1
|
346
|
-
if (typeof action.insert === 'string') {
|
347
|
-
length = action.insert.length
|
348
|
-
slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes))
|
349
|
-
} else {
|
350
|
-
const sharedComponent = action.insert as YMap<any>
|
351
|
-
const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent)
|
352
|
-
const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent)
|
353
|
-
this.syncSlots(sharedComponent.get('slots'), component)
|
354
|
-
this.syncComponent(sharedComponent, component)
|
355
|
-
slot.insert(component)
|
356
|
-
}
|
357
|
-
if (this.selection.isSelected) {
|
358
|
-
if (slot === this.selection.anchorSlot && this.selection.anchorOffset! > index) {
|
359
|
-
this.selection.setAnchor(slot, this.selection.anchorOffset! + length)
|
360
|
-
}
|
361
|
-
if (slot === this.selection.focusSlot && this.selection.focusOffset! > index) {
|
362
|
-
this.selection.setFocus(slot, this.selection.focusOffset! + length)
|
363
|
-
}
|
364
|
-
}
|
365
|
-
} else if (action.delete) {
|
366
|
-
const index = slot.index
|
367
|
-
slot.retain(slot.index)
|
368
|
-
slot.delete(action.delete)
|
369
|
-
if (this.selection.isSelected) {
|
370
|
-
if (slot === this.selection.anchorSlot && this.selection.anchorOffset! >= index) {
|
371
|
-
this.selection.setAnchor(slot, this.selection.startOffset! - action.delete)
|
372
|
-
}
|
373
|
-
if (slot === this.selection.focusSlot && this.selection.focusOffset! >= index) {
|
374
|
-
this.selection.setFocus(slot, this.selection.focusOffset! - action.delete)
|
375
|
-
}
|
376
|
-
}
|
377
|
-
} else if (action.attributes) {
|
378
|
-
slot.updateState(draft => {
|
379
|
-
if (typeof draft === 'object' && draft !== null) {
|
380
|
-
Object.assign(draft, action.attributes)
|
381
|
-
} else {
|
382
|
-
return action.attributes
|
383
|
-
}
|
384
|
-
})
|
385
|
-
}
|
386
|
-
})
|
387
|
-
})
|
388
|
-
}
|
389
|
-
content.observe(syncRemote)
|
390
|
-
|
391
|
-
const sub = slot.onContentChange.subscribe(actions => {
|
392
|
-
this.runLocalUpdate(() => {
|
393
|
-
let offset = 0
|
394
|
-
let length = 0
|
395
|
-
for (const action of actions) {
|
396
|
-
if (action.type === 'retain') {
|
397
|
-
const formats = action.formats
|
398
|
-
if (formats) {
|
399
|
-
const keys = Object.keys(formats)
|
400
|
-
let length = keys.length
|
401
|
-
keys.forEach(key => {
|
402
|
-
const formatter = this.registry.getFormatter(key)
|
403
|
-
if (!formatter) {
|
404
|
-
length--
|
405
|
-
Reflect.deleteProperty(formats, key)
|
406
|
-
}
|
407
|
-
})
|
408
|
-
if (length) {
|
409
|
-
content.format(offset, action.offset, formats)
|
410
|
-
}
|
411
|
-
} else {
|
412
|
-
offset = action.offset
|
413
|
-
}
|
414
|
-
} else if (action.type === 'insert') {
|
415
|
-
const delta = content.toDelta()
|
416
|
-
const isEmpty = delta.length === 1 && delta[0].insert === Slot.emptyPlaceholder
|
417
|
-
if (typeof action.content === 'string') {
|
418
|
-
length = action.content.length
|
419
|
-
content.insert(offset, action.content, action.formats || {})
|
420
|
-
} else {
|
421
|
-
length = 1
|
422
|
-
const sharedComponent = this.createSharedComponentByComponent(action.ref as ComponentInstance)
|
423
|
-
content.insertEmbed(offset, sharedComponent, action.formats || {})
|
424
|
-
}
|
425
|
-
|
426
|
-
if (isEmpty && offset === 0) {
|
427
|
-
content.delete(content.length - 1, 1)
|
428
|
-
}
|
429
|
-
offset += length
|
430
|
-
} else if (action.type === 'delete') {
|
431
|
-
const delta = content.toDelta()
|
432
|
-
if (content.length) {
|
433
|
-
content.delete(offset, action.count)
|
434
|
-
}
|
435
|
-
if (content.length === 0) {
|
436
|
-
content.insert(0, '\n', delta[0]?.attributes)
|
437
|
-
}
|
438
|
-
}
|
439
|
-
}
|
440
|
-
})
|
441
|
-
})
|
442
|
-
|
443
|
-
sub.add(slot.onChildComponentRemove.subscribe(components => {
|
444
|
-
components.forEach(c => {
|
445
|
-
this.cleanSubscriptionsByComponent(c)
|
446
|
-
})
|
447
|
-
}))
|
448
|
-
this.contentSyncCaches.set(slot, () => {
|
449
|
-
content.unobserve(syncRemote)
|
450
|
-
sub.unsubscribe()
|
451
|
-
})
|
452
|
-
}
|
453
|
-
|
454
|
-
protected syncSlot(remoteSlot: YMap<any>, slot: Slot) {
|
455
|
-
const syncRemote = (ev, tr) => {
|
456
|
-
this.runRemoteUpdate(tr, () => {
|
457
|
-
ev.keysChanged.forEach(key => {
|
458
|
-
if (key === 'state') {
|
459
|
-
const state = (ev.target as YMap<any>).get('state')
|
460
|
-
slot.updateState(draft => {
|
461
|
-
if (typeof draft === 'object' && draft !== null) {
|
462
|
-
Object.assign(draft, state)
|
463
|
-
} else {
|
464
|
-
return state
|
465
|
-
}
|
466
|
-
})
|
467
|
-
}
|
468
|
-
})
|
469
|
-
})
|
470
|
-
}
|
471
|
-
remoteSlot.observe(syncRemote)
|
472
|
-
|
473
|
-
const sub = slot.onStateChange.subscribe(change => {
|
474
|
-
this.runLocalUpdate(() => {
|
475
|
-
remoteSlot.set('state', change.newState)
|
476
|
-
}, change.record)
|
477
|
-
})
|
478
|
-
this.slotStateSyncCaches.set(slot, () => {
|
479
|
-
remoteSlot.unobserve(syncRemote)
|
480
|
-
sub.unsubscribe()
|
481
|
-
})
|
482
|
-
}
|
483
|
-
|
484
|
-
protected syncSlots(remoteSlots: YArray<any>, component: ComponentInstance) {
|
485
|
-
const slots = component.slots
|
486
|
-
const syncRemote = (ev, tr) => {
|
487
|
-
this.runRemoteUpdate(tr, () => {
|
488
|
-
let index = 0
|
489
|
-
ev.delta.forEach(action => {
|
490
|
-
if (Reflect.has(action, 'retain')) {
|
491
|
-
index += action.retain
|
492
|
-
slots.retain(index)
|
493
|
-
} else if (action.insert) {
|
494
|
-
(action.insert as Array<YMap<any>>).forEach(item => {
|
495
|
-
const slot = this.createSlotBySharedSlot(item)
|
496
|
-
slots.insert(slot)
|
497
|
-
this.syncContent(item.get('content'), slot)
|
498
|
-
this.syncSlot(item, slot)
|
499
|
-
index++
|
500
|
-
})
|
501
|
-
} else if (action.delete) {
|
502
|
-
slots.retain(index)
|
503
|
-
slots.delete(action.delete)
|
504
|
-
}
|
505
|
-
})
|
506
|
-
})
|
507
|
-
}
|
508
|
-
remoteSlots.observe(syncRemote)
|
509
|
-
|
510
|
-
const sub = slots.onChange.subscribe(operations => {
|
511
|
-
this.runLocalUpdate(() => {
|
512
|
-
const applyActions = operations.apply
|
513
|
-
let index: number
|
514
|
-
applyActions.forEach(action => {
|
515
|
-
if (action.type === 'retain') {
|
516
|
-
index = action.offset
|
517
|
-
} else if (action.type === 'insertSlot') {
|
518
|
-
const sharedSlot = this.createSharedSlotBySlot(action.ref)
|
519
|
-
remoteSlots.insert(index, [sharedSlot])
|
520
|
-
index++
|
521
|
-
} else if (action.type === 'delete') {
|
522
|
-
remoteSlots.delete(index, action.count)
|
523
|
-
}
|
524
|
-
})
|
525
|
-
})
|
526
|
-
})
|
527
|
-
|
528
|
-
sub.add(slots.onChildSlotRemove.subscribe(slots => {
|
529
|
-
slots.forEach(slot => {
|
530
|
-
this.cleanSubscriptionsBySlot(slot)
|
531
|
-
})
|
532
|
-
}))
|
533
|
-
|
534
|
-
this.slotsSyncCaches.set(component, () => {
|
535
|
-
remoteSlots.unobserve(syncRemote)
|
536
|
-
sub.unsubscribe()
|
537
|
-
})
|
538
|
-
}
|
539
|
-
|
540
|
-
protected syncComponent(remoteComponent: YMap<any>, component: ComponentInstance) {
|
541
|
-
const syncRemote = (ev, tr) => {
|
542
|
-
this.runRemoteUpdate(tr, () => {
|
543
|
-
ev.keysChanged.forEach(key => {
|
544
|
-
if (key === 'state') {
|
545
|
-
const state = (ev.target as YMap<any>).get('state')
|
546
|
-
component.updateState(draft => {
|
547
|
-
if (typeof draft === 'object' && draft !== null) {
|
548
|
-
Object.assign(draft, state)
|
549
|
-
} else {
|
550
|
-
return state
|
551
|
-
}
|
552
|
-
})
|
553
|
-
}
|
554
|
-
})
|
555
|
-
})
|
556
|
-
}
|
557
|
-
remoteComponent.observe(syncRemote)
|
558
|
-
|
559
|
-
const sub = component.onStateChange.subscribe(change => {
|
560
|
-
this.runLocalUpdate(() => {
|
561
|
-
remoteComponent.set('state', change.newState)
|
562
|
-
}, change.record)
|
563
|
-
})
|
564
|
-
this.componentStateSyncCaches.set(component, () => {
|
565
|
-
remoteComponent.unobserve(syncRemote)
|
566
|
-
sub.unsubscribe()
|
567
|
-
})
|
568
|
-
}
|
569
|
-
|
570
|
-
protected runLocalUpdate(fn: () => void, record = true) {
|
571
|
-
if (this.updateFromRemote || this.controller.readonly) {
|
572
|
-
return
|
573
|
-
}
|
574
|
-
this.updateRemoteActions.push({
|
575
|
-
record,
|
576
|
-
action: fn
|
577
|
-
})
|
578
|
-
}
|
579
|
-
|
580
|
-
protected runRemoteUpdate(tr: Transaction, fn: () => void) {
|
581
|
-
if (tr.origin === this.yDoc) {
|
582
|
-
return
|
583
|
-
}
|
584
|
-
this.updateFromRemote = true
|
585
|
-
if (tr.origin === this.manager) {
|
586
|
-
this.scheduler.historyApplyTransact(fn)
|
587
|
-
} else {
|
588
|
-
this.scheduler.remoteUpdateTransact(fn)
|
589
|
-
}
|
590
|
-
this.updateFromRemote = false
|
591
|
-
}
|
592
|
-
|
593
|
-
protected createSharedComponentByComponent(component: ComponentInstance): YMap<any> {
|
594
|
-
const sharedComponent = new YMap()
|
595
|
-
sharedComponent.set('state', component.state)
|
596
|
-
sharedComponent.set('name', component.name)
|
597
|
-
const sharedSlots = new YArray()
|
598
|
-
sharedComponent.set('slots', sharedSlots)
|
599
|
-
component.slots.toArray().forEach(slot => {
|
600
|
-
const sharedSlot = this.createSharedSlotBySlot(slot)
|
601
|
-
sharedSlots.push([sharedSlot])
|
602
|
-
})
|
603
|
-
this.syncSlots(sharedSlots, component)
|
604
|
-
this.syncComponent(sharedComponent, component)
|
605
|
-
return sharedComponent
|
606
|
-
}
|
607
|
-
|
608
|
-
protected createSharedSlotBySlot(slot: Slot): YMap<any> {
|
609
|
-
const sharedSlot = new YMap()
|
610
|
-
sharedSlot.set('schema', slot.schema)
|
611
|
-
sharedSlot.set('state', slot.state)
|
612
|
-
const sharedContent = new YText()
|
613
|
-
sharedSlot.set('content', sharedContent)
|
614
|
-
let offset = 0
|
615
|
-
slot.toDelta().forEach(i => {
|
616
|
-
let formats: any = {}
|
617
|
-
if (i.formats) {
|
618
|
-
i.formats.forEach(item => {
|
619
|
-
formats[item[0].name] = item[1]
|
620
|
-
})
|
621
|
-
} else {
|
622
|
-
formats = null
|
623
|
-
}
|
624
|
-
if (typeof i.insert === 'string') {
|
625
|
-
sharedContent.insert(offset, i.insert, formats)
|
626
|
-
} else {
|
627
|
-
const sharedComponent = this.createSharedComponentByComponent(i.insert)
|
628
|
-
sharedContent.insertEmbed(offset, sharedComponent, formats)
|
629
|
-
}
|
630
|
-
offset += i.insert.length
|
631
|
-
})
|
632
|
-
this.syncContent(sharedContent, slot)
|
633
|
-
this.syncSlot(sharedSlot, slot)
|
634
|
-
return sharedSlot
|
635
|
-
}
|
636
|
-
|
637
|
-
protected createComponentBySharedComponent(yMap: YMap<any>, canInsertInlineComponent: boolean): ComponentInstance {
|
638
|
-
const sharedSlots = yMap.get('slots') as YArray<YMap<any>>
|
639
|
-
const slots: Slot[] = []
|
640
|
-
sharedSlots.forEach(sharedSlot => {
|
641
|
-
const slot = this.createSlotBySharedSlot(sharedSlot)
|
642
|
-
slots.push(slot)
|
643
|
-
})
|
644
|
-
const name = yMap.get('name')
|
645
|
-
const state = yMap.get('state')
|
646
|
-
const instance = this.translator.createComponentByData(name, {
|
647
|
-
state,
|
648
|
-
slots
|
649
|
-
})
|
650
|
-
if (instance) {
|
651
|
-
instance.slots.toArray().forEach((slot, index) => {
|
652
|
-
let sharedSlot = sharedSlots.get(index)
|
653
|
-
if (!sharedSlot) {
|
654
|
-
sharedSlot = this.createSharedSlotBySlot(slot)
|
655
|
-
sharedSlots.push([sharedSlot])
|
656
|
-
}
|
657
|
-
this.syncSlot(sharedSlot, slot)
|
658
|
-
this.syncContent(sharedSlot.get('content'), slot)
|
659
|
-
})
|
660
|
-
return instance
|
661
|
-
}
|
662
|
-
return createUnknownComponent(name, canInsertInlineComponent).createInstance(this.starter)
|
663
|
-
}
|
664
|
-
|
665
|
-
protected createSlotBySharedSlot(sharedSlot: YMap<any>): Slot {
|
666
|
-
const content = sharedSlot.get('content') as YText
|
667
|
-
const delta = content.toDelta()
|
668
|
-
|
669
|
-
const slot = this.translator.createSlot({
|
670
|
-
schema: sharedSlot.get('schema'),
|
671
|
-
state: sharedSlot.get('state'),
|
672
|
-
formats: {},
|
673
|
-
content: []
|
674
|
-
})
|
675
|
-
|
676
|
-
for (const action of delta) {
|
677
|
-
if (action.insert) {
|
678
|
-
if (typeof action.insert === 'string') {
|
679
|
-
slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes))
|
680
|
-
} else {
|
681
|
-
const sharedComponent = action.insert as YMap<any>
|
682
|
-
const canInsertInlineComponent = slot.schema.includes(ContentType.InlineComponent)
|
683
|
-
const component = this.createComponentBySharedComponent(sharedComponent, canInsertInlineComponent)
|
684
|
-
slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes))
|
685
|
-
this.syncSlots(sharedComponent.get('slots'), component)
|
686
|
-
this.syncComponent(sharedComponent, component)
|
687
|
-
}
|
688
|
-
} else {
|
689
|
-
throw collaborateErrorFn('unexpected delta action.')
|
690
|
-
}
|
691
|
-
}
|
692
|
-
return slot
|
693
|
-
}
|
694
|
-
|
695
|
-
protected cleanSubscriptionsBySlot(slot: Slot) {
|
696
|
-
this.contentMap.delete(slot);
|
697
|
-
[this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
|
698
|
-
if (fn) {
|
699
|
-
fn()
|
700
|
-
}
|
701
|
-
})
|
702
|
-
slot.sliceContent().forEach(i => {
|
703
|
-
if (typeof i !== 'string') {
|
704
|
-
this.cleanSubscriptionsByComponent(i)
|
705
|
-
}
|
706
|
-
})
|
707
|
-
}
|
708
|
-
|
709
|
-
protected cleanSubscriptionsByComponent(component: ComponentInstance) {
|
710
|
-
[this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
|
711
|
-
if (fn) {
|
712
|
-
fn()
|
713
|
-
}
|
714
|
-
})
|
715
|
-
component.slots.toArray().forEach(slot => {
|
716
|
-
this.cleanSubscriptionsBySlot(slot)
|
717
|
-
})
|
718
|
-
}
|
719
|
-
}
|
720
|
-
|
721
|
-
function remoteFormatsToLocal(registry: Registry, attrs?: any,) {
|
722
|
-
const formats: Formats = []
|
723
|
-
if (attrs) {
|
724
|
-
Object.keys(attrs).forEach(key => {
|
725
|
-
const formatter = registry.getFormatter(key)
|
726
|
-
if (formatter) {
|
727
|
-
formats.push([formatter, attrs[key]])
|
728
|
-
}
|
729
|
-
})
|
730
|
-
}
|
731
|
-
return formats
|
732
|
-
}
|
package/src/public-api.ts
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
import { History, Module } from '@textbus/core'
|
2
|
-
|
3
|
-
import { Collaborate } from './collaborate'
|
4
|
-
import { CollaborateCursor } from './collaborate-cursor'
|
5
|
-
|
6
|
-
export * from './collaborate'
|
7
|
-
export * from './collaborate-cursor'
|
8
|
-
|
9
|
-
export const collaborateModule: Module = {
|
10
|
-
providers: [
|
11
|
-
Collaborate,
|
12
|
-
CollaborateCursor,
|
13
|
-
{
|
14
|
-
provide: History,
|
15
|
-
useClass: Collaborate
|
16
|
-
}
|
17
|
-
]
|
18
|
-
}
|
package/src/unknown.component.ts
DELETED
@@ -1,22 +0,0 @@
|
|
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/tsconfig-build.json
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"compilerOptions": {
|
3
|
-
"declaration": true,
|
4
|
-
"useDefineForClassFields": false,
|
5
|
-
"emitDecoratorMetadata": true,
|
6
|
-
"experimentalDecorators": true,
|
7
|
-
"allowSyntheticDefaultImports": true,
|
8
|
-
"lib": ["esnext", "dom"],
|
9
|
-
"target": "es6",
|
10
|
-
"strict": true,
|
11
|
-
"module": "es2020",
|
12
|
-
"moduleResolution": "node",
|
13
|
-
"inlineSourceMap": true,
|
14
|
-
"inlineSources": true,
|
15
|
-
"noImplicitAny": false,
|
16
|
-
"suppressImplicitAnyIndexErrors": true,
|
17
|
-
"outDir": "bundles/",
|
18
|
-
"downlevelIteration": true,
|
19
|
-
"jsx": "react",
|
20
|
-
"jsxFactory": "VElement.createElement",
|
21
|
-
"paths": {
|
22
|
-
"@textbus/collaborate": ["./src/public-api.ts"],
|
23
|
-
}
|
24
|
-
},
|
25
|
-
"include": [
|
26
|
-
"src/"
|
27
|
-
]
|
28
|
-
}
|