@textbus/collaborate 2.0.0-beta.6 → 2.0.0-beta.60

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,4 @@
1
+ import { Module } from '@textbus/core';
1
2
  export * from './collaborate';
2
3
  export * from './collaborate-cursor';
4
+ export declare const collaborateModule: Module;
@@ -1,3 +1,16 @@
1
+ import { History } from '@textbus/core';
2
+ import { Collaborate } from './collaborate';
3
+ import { CollaborateCursor } from './collaborate-cursor';
1
4
  export * from './collaborate';
2
5
  export * from './collaborate-cursor';
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsZUFBZSxDQUFBO0FBQzdCLGNBQWMsc0JBQXNCLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL2NvbGxhYm9yYXRlJ1xuZXhwb3J0ICogZnJvbSAnLi9jb2xsYWJvcmF0ZS1jdXJzb3InXG4iXX0=
6
+ export const collaborateModule = {
7
+ providers: [
8
+ Collaborate,
9
+ CollaborateCursor,
10
+ {
11
+ provide: History,
12
+ useClass: Collaborate
13
+ }
14
+ ]
15
+ };
16
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wdWJsaWMtYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxPQUFPLEVBQVUsTUFBTSxlQUFlLENBQUE7QUFFL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGVBQWUsQ0FBQTtBQUMzQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQTtBQUV4RCxjQUFjLGVBQWUsQ0FBQTtBQUM3QixjQUFjLHNCQUFzQixDQUFBO0FBRXBDLE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFXO0lBQ3ZDLFNBQVMsRUFBRTtRQUNULFdBQVc7UUFDWCxpQkFBaUI7UUFDakI7WUFDRSxPQUFPLEVBQUUsT0FBTztZQUNoQixRQUFRLEVBQUUsV0FBVztTQUN0QjtLQUNGO0NBQ0YsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEhpc3RvcnksIE1vZHVsZSB9IGZyb20gJ0B0ZXh0YnVzL2NvcmUnXG5cbmltcG9ydCB7IENvbGxhYm9yYXRlIH0gZnJvbSAnLi9jb2xsYWJvcmF0ZSdcbmltcG9ydCB7IENvbGxhYm9yYXRlQ3Vyc29yIH0gZnJvbSAnLi9jb2xsYWJvcmF0ZS1jdXJzb3InXG5cbmV4cG9ydCAqIGZyb20gJy4vY29sbGFib3JhdGUnXG5leHBvcnQgKiBmcm9tICcuL2NvbGxhYm9yYXRlLWN1cnNvcidcblxuZXhwb3J0IGNvbnN0IGNvbGxhYm9yYXRlTW9kdWxlOiBNb2R1bGUgPSB7XG4gIHByb3ZpZGVyczogW1xuICAgIENvbGxhYm9yYXRlLFxuICAgIENvbGxhYm9yYXRlQ3Vyc29yLFxuICAgIHtcbiAgICAgIHByb3ZpZGU6IEhpc3RvcnksXG4gICAgICB1c2VDbGFzczogQ29sbGFib3JhdGVcbiAgICB9XG4gIF1cbn1cbiJdfQ==
@@ -0,0 +1 @@
1
+ export declare function createUnknownComponent(factoryName: string, canInsertInlineComponent: boolean): any;
@@ -0,0 +1,22 @@
1
+ import { ContentType, defineComponent, VElement } from '@textbus/core';
2
+ export function createUnknownComponent(factoryName, canInsertInlineComponent) {
3
+ const unknownComponent = defineComponent({
4
+ type: canInsertInlineComponent ? ContentType.InlineComponent : ContentType.BlockComponent,
5
+ name: 'UnknownComponent',
6
+ setup() {
7
+ console.error(`cannot find component factory \`${factoryName}\`.`);
8
+ return {
9
+ render() {
10
+ return VElement.createElement('textbus-unknown-component', {
11
+ style: {
12
+ display: canInsertInlineComponent ? 'inline' : 'block',
13
+ color: '#f00'
14
+ }
15
+ }, unknownComponent.name);
16
+ }
17
+ };
18
+ }
19
+ });
20
+ return unknownComponent;
21
+ }
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5rbm93bi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdW5rbm93bi5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBRXRFLE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxXQUFtQixFQUFFLHdCQUFpQztJQUMzRixNQUFNLGdCQUFnQixHQUFHLGVBQWUsQ0FBQztRQUN2QyxJQUFJLEVBQUUsd0JBQXdCLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxjQUFjO1FBQ3pGLElBQUksRUFBRSxrQkFBa0I7UUFDeEIsS0FBSztZQUNILE9BQU8sQ0FBQyxLQUFLLENBQUMsbUNBQW1DLFdBQVcsS0FBSyxDQUFDLENBQUE7WUFDbEUsT0FBTztnQkFDTCxNQUFNO29CQUNKLE9BQU8sUUFBUSxDQUFDLGFBQWEsQ0FBQywyQkFBMkIsRUFBRTt3QkFDekQsS0FBSyxFQUFFOzRCQUNMLE9BQU8sRUFBRSx3QkFBd0IsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPOzRCQUN0RCxLQUFLLEVBQUUsTUFBTTt5QkFDZDtxQkFDRixFQUFFLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFBO2dCQUMzQixDQUFDO2FBQ0YsQ0FBQTtRQUNILENBQUM7S0FDRixDQUFDLENBQUE7SUFDRixPQUFPLGdCQUFnQixDQUFBO0FBQ3pCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb250ZW50VHlwZSwgZGVmaW5lQ29tcG9uZW50LCBWRWxlbWVudCB9IGZyb20gJ0B0ZXh0YnVzL2NvcmUnXG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVVbmtub3duQ29tcG9uZW50KGZhY3RvcnlOYW1lOiBzdHJpbmcsIGNhbkluc2VydElubGluZUNvbXBvbmVudDogYm9vbGVhbikge1xuICBjb25zdCB1bmtub3duQ29tcG9uZW50ID0gZGVmaW5lQ29tcG9uZW50KHtcbiAgICB0eXBlOiBjYW5JbnNlcnRJbmxpbmVDb21wb25lbnQgPyBDb250ZW50VHlwZS5JbmxpbmVDb21wb25lbnQgOiBDb250ZW50VHlwZS5CbG9ja0NvbXBvbmVudCxcbiAgICBuYW1lOiAnVW5rbm93bkNvbXBvbmVudCcsXG4gICAgc2V0dXAoKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBjYW5ub3QgZmluZCBjb21wb25lbnQgZmFjdG9yeSBcXGAke2ZhY3RvcnlOYW1lfVxcYC5gKVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcmVuZGVyKCkge1xuICAgICAgICAgIHJldHVybiBWRWxlbWVudC5jcmVhdGVFbGVtZW50KCd0ZXh0YnVzLXVua25vd24tY29tcG9uZW50Jywge1xuICAgICAgICAgICAgc3R5bGU6IHtcbiAgICAgICAgICAgICAgZGlzcGxheTogY2FuSW5zZXJ0SW5saW5lQ29tcG9uZW50ID8gJ2lubGluZScgOiAnYmxvY2snLFxuICAgICAgICAgICAgICBjb2xvcjogJyNmMDAnXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSwgdW5rbm93bkNvbXBvbmVudC5uYW1lKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9KVxuICByZXR1cm4gdW5rbm93bkNvbXBvbmVudFxufVxuIl19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@textbus/collaborate",
3
- "version": "2.0.0-beta.6",
3
+ "version": "2.0.0-beta.60",
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,13 +25,13 @@
25
25
  "typescript editor"
26
26
  ],
27
27
  "dependencies": {
28
- "@tanbo/di": "^1.1.0",
29
- "@tanbo/stream": "^1.0.0",
30
- "@textbus/browser": "^2.0.0-beta.6",
31
- "@textbus/core": "^2.0.0-beta.6",
28
+ "@tanbo/di": "^1.1.3",
29
+ "@tanbo/stream": "^1.1.5",
30
+ "@textbus/browser": "^2.0.0-beta.60",
31
+ "@textbus/core": "^2.0.0-beta.60",
32
32
  "reflect-metadata": "^0.1.13",
33
33
  "y-protocols": "^1.0.5",
34
- "yjs": "^13.5.27"
34
+ "yjs": "^13.5.39"
35
35
  },
36
36
  "author": {
37
37
  "name": "Tanbo",
@@ -44,5 +44,5 @@
44
44
  "bugs": {
45
45
  "url": "https://github.com/textbus/textbus.git/issues"
46
46
  },
47
- "gitHead": "d7041c011b2b507fc0a52f6b505057293373eeed"
47
+ "gitHead": "f0b2eea526bb5cea6b13036c1f689b79d4e5edc9"
48
48
  }
@@ -1,13 +1,12 @@
1
1
  import { Inject, Injectable, Optional } from '@tanbo/di'
2
2
  import {
3
3
  createElement,
4
- EDITABLE_DOCUMENT,
5
- EDITOR_CONTAINER,
4
+ VIEW_CONTAINER,
6
5
  getLayoutRectByRange,
7
6
  SelectionBridge
8
7
  } from '@textbus/browser'
9
- import { Selection, SelectionPaths, Range as TBRange } from '@textbus/core'
10
- import { Subject } from '@tanbo/stream'
8
+ import { Selection, SelectionPaths, Range as TBRange, Scheduler } from '@textbus/core'
9
+ import { fromEvent, Subject, Subscription } from '@tanbo/stream'
11
10
 
12
11
  export interface RemoteSelection {
13
12
  color: string
@@ -33,12 +32,33 @@ export interface RemoteSelectionCursor {
33
32
  userTip: HTMLElement
34
33
  }
35
34
 
36
- export abstract class CollaborateCursorAwarenessDelegate {
37
- abstract getRects(range: TBRange, nativeRange: Range): null | Rect[]
35
+ export abstract class CollaborateSelectionAwarenessDelegate {
36
+ abstract getRects(range: TBRange, nativeRange: Range): false | Rect[]
38
37
  }
39
38
 
40
39
  @Injectable()
41
40
  export class CollaborateCursor {
41
+ private host = createElement('div', {
42
+ styles: {
43
+ position: 'absolute',
44
+ left: 0,
45
+ top: 0,
46
+ width: '100%',
47
+ height: '100%',
48
+ pointerEvents: 'none',
49
+ zIndex: 1
50
+ }
51
+ })
52
+ private canvasContainer = createElement('div', {
53
+ styles: {
54
+ position: 'absolute',
55
+ left: 0,
56
+ top: 0,
57
+ width: '100%',
58
+ height: '100%',
59
+ overflow: 'hidden'
60
+ }
61
+ })
42
62
  private canvas = createElement('canvas', {
43
63
  styles: {
44
64
  position: 'absolute',
@@ -46,8 +66,8 @@ export class CollaborateCursor {
46
66
  left: 0,
47
67
  top: 0,
48
68
  width: '100%',
49
- height: '100%',
50
- pointerEvents: 'none'
69
+ height: document.documentElement.clientHeight + 'px',
70
+ pointerEvents: 'none',
51
71
  }
52
72
  }) as HTMLCanvasElement
53
73
  private context = this.canvas.getContext('2d')!
@@ -66,13 +86,18 @@ export class CollaborateCursor {
66
86
 
67
87
  private onRectsChange = new Subject<SelectionRect[]>()
68
88
 
69
- constructor(@Inject(EDITOR_CONTAINER) private container: HTMLElement,
70
- @Inject(EDITABLE_DOCUMENT) private document: Document,
71
- @Optional() private awarenessDelegate: CollaborateCursorAwarenessDelegate,
89
+ private subscription = new Subscription()
90
+ private currentSelection: RemoteSelection[] = []
91
+
92
+ constructor(@Inject(VIEW_CONTAINER) private container: HTMLElement,
93
+ @Optional() private awarenessDelegate: CollaborateSelectionAwarenessDelegate,
72
94
  private nativeSelection: SelectionBridge,
95
+ private scheduler: Scheduler,
73
96
  private selection: Selection) {
74
- container.prepend(this.canvas, this.tooltips)
75
- this.onRectsChange.subscribe(rects => {
97
+ this.canvasContainer.append(this.canvas)
98
+ this.host.append(this.canvasContainer, this.tooltips)
99
+ container.prepend(this.host)
100
+ this.subscription.add(this.onRectsChange.subscribe(rects => {
76
101
  for (const rect of rects) {
77
102
  this.context.fillStyle = rect.color
78
103
  this.context.beginPath()
@@ -80,50 +105,73 @@ export class CollaborateCursor {
80
105
  this.context.fill()
81
106
  this.context.closePath()
82
107
  }
83
- })
108
+ }), fromEvent(window, 'resize').subscribe(() => {
109
+ this.canvas.style.height = document.documentElement.clientHeight + 'px'
110
+ this.refresh()
111
+ }), this.scheduler.onDocChanged.subscribe(() => {
112
+ this.refresh()
113
+ }))
114
+ }
115
+
116
+ refresh() {
117
+ this.draw(this.currentSelection)
118
+ }
119
+
120
+ destroy() {
121
+ this.subscription.unsubscribe()
84
122
  }
85
123
 
86
124
  draw(paths: RemoteSelection[]) {
125
+ this.currentSelection = paths
87
126
  const containerRect = this.container.getBoundingClientRect()
88
- this.canvas.width = containerRect.width
89
- this.canvas.height = containerRect.height
127
+ this.canvas.style.top = containerRect.y * -1 + 'px'
128
+ this.canvas.width = this.canvas.offsetWidth
129
+ this.canvas.height = this.canvas.offsetHeight
90
130
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
91
131
 
92
132
  const users: SelectionRect[] = []
93
133
 
94
-
95
134
  paths.filter(i => {
96
- return i.paths.start.length && i.paths.end.length
135
+ return i.paths.anchor.length && i.paths.focus.length
97
136
  }).forEach(item => {
98
- const startOffset = item.paths.start.pop()!
99
- const startSlot = this.selection.findSlotByPaths(item.paths.start)
100
- const endOffset = item.paths.end.pop()!
101
- const endSlot = this.selection.findSlotByPaths(item.paths.end)
102
- if (!startSlot || !endSlot) {
137
+ const anchorPaths = [...item.paths.anchor]
138
+ const focusPaths = [...item.paths.focus]
139
+ const anchorOffset = anchorPaths.pop()!
140
+ const anchorSlot = this.selection.findSlotByPaths(anchorPaths)
141
+ const focusOffset = focusPaths.pop()!
142
+ const focusSlot = this.selection.findSlotByPaths(focusPaths)
143
+ if (!anchorSlot || !focusSlot) {
103
144
  return
104
145
  }
105
146
 
106
- const {start, end} = this.nativeSelection.getPositionByRange({
107
- startOffset,
108
- endOffset,
109
- startSlot,
110
- endSlot
147
+ const { focus, anchor } = this.nativeSelection.getPositionByRange({
148
+ focusOffset,
149
+ anchorOffset,
150
+ focusSlot,
151
+ anchorSlot
111
152
  })
112
- if (!start || !end) {
153
+ if (!focus || !anchor) {
113
154
  return
114
155
  }
115
- const nativeRange = this.document.createRange()
116
- nativeRange.setStart(start.node, start.offset)
117
- nativeRange.setEnd(end.node, end.offset)
118
-
119
- const rects = this.awarenessDelegate ? this.awarenessDelegate.getRects({
120
- startOffset,
121
- endOffset,
122
- startSlot,
123
- endSlot
124
- }, nativeRange) : nativeRange.getClientRects()
156
+ const nativeRange = document.createRange()
157
+ nativeRange.setStart(anchor.node, anchor.offset)
158
+ nativeRange.setEnd(focus.node, focus.offset)
159
+ if ((anchor.node !== focus.node || anchor.offset !== focus.offset) && nativeRange.collapsed) {
160
+ nativeRange.setStart(focus.node, focus.offset)
161
+ nativeRange.setEnd(anchor.node, anchor.offset)
162
+ }
163
+
164
+ let rects: Rect[] | DOMRectList | false = false
165
+ if (this.awarenessDelegate) {
166
+ rects = this.awarenessDelegate.getRects({
167
+ focusOffset,
168
+ anchorOffset,
169
+ focusSlot,
170
+ anchorSlot
171
+ }, nativeRange)
172
+ }
125
173
  if (!rects) {
126
- return
174
+ rects = nativeRange.getClientRects()
127
175
  }
128
176
  const selectionRects: SelectionRect[] = []
129
177
  for (let i = rects.length - 1; i >= 0; i--) {
@@ -132,7 +180,7 @@ export class CollaborateCursor {
132
180
  color: item.color,
133
181
  username: item.username,
134
182
  x: rect.x - containerRect.x,
135
- y: rect.y - containerRect.y,
183
+ y: rect.y,
136
184
  width: rect.width,
137
185
  height: rect.height,
138
186
  })
@@ -140,18 +188,23 @@ export class CollaborateCursor {
140
188
  this.onRectsChange.next(selectionRects)
141
189
 
142
190
  const cursorRange = nativeRange.cloneRange()
143
- cursorRange.collapse(!item.paths.focusEnd)
191
+ cursorRange.setStart(focus.node, focus.offset)
192
+ cursorRange.collapse(true)
144
193
 
145
194
  const cursorRect = getLayoutRectByRange(cursorRange)
146
195
 
147
- users.push({
196
+ const rect: SelectionRect = {
148
197
  username: item.username,
149
198
  color: item.color,
150
199
  x: cursorRect.x - containerRect.x,
151
200
  y: cursorRect.y - containerRect.y,
152
201
  width: 2,
153
202
  height: cursorRect.height
154
- })
203
+ }
204
+ if (rect.x < 0 || rect.y < 0 || rect.x > containerRect.width) {
205
+ return
206
+ }
207
+ users.push(rect)
155
208
  })
156
209
  this.drawUserCursor(users)
157
210
  }
@@ -159,7 +212,7 @@ export class CollaborateCursor {
159
212
  private drawUserCursor(rects: SelectionRect[]) {
160
213
  for (let i = 0; i < rects.length; i++) {
161
214
  const rect = rects[i]
162
- const {cursor, userTip, anchor} = this.getUserCursor(i)
215
+ const { cursor, userTip, anchor } = this.getUserCursor(i)
163
216
  Object.assign(cursor.style, {
164
217
  left: rect.x + 'px',
165
218
  top: rect.y + 'px',