@netless/window-manager 0.3.18 → 0.4.0-canary.11
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/CHANGELOG.md +6 -0
- package/README.md +4 -43
- package/dist/App/MagixEvent/index.d.ts +28 -0
- package/dist/App/Storage/StorageEvent.d.ts +8 -0
- package/dist/App/Storage/index.d.ts +38 -0
- package/dist/App/Storage/typings.d.ts +21 -0
- package/dist/App/Storage/utils.d.ts +5 -0
- package/dist/AppContext.d.ts +42 -17
- package/dist/AppListener.d.ts +2 -2
- package/dist/AppManager.d.ts +18 -14
- package/dist/AppProxy.d.ts +5 -3
- package/dist/AttributesDelegate.d.ts +19 -11
- package/dist/Base/Context.d.ts +0 -1
- package/dist/Base/index.d.ts +1 -2
- package/dist/BoxManager.d.ts +24 -7
- package/dist/BuiltinApps.d.ts +6 -0
- package/dist/ContainerResizeObserver.d.ts +10 -0
- package/dist/Cursor/Cursor.d.ts +2 -3
- package/dist/Cursor/index.d.ts +9 -5
- package/dist/Helper.d.ts +6 -0
- package/dist/ReconnectRefresher.d.ts +9 -3
- package/dist/Utils/Common.d.ts +3 -1
- package/dist/Utils/Reactive.d.ts +1 -1
- package/dist/Utils/RoomHacker.d.ts +2 -2
- package/dist/Utils/error.d.ts +3 -0
- package/dist/{MainView.d.ts → View/MainView.d.ts} +3 -4
- package/dist/View/ViewManager.d.ts +13 -0
- package/dist/constants.d.ts +3 -7
- package/dist/index.d.ts +25 -27
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/typings.d.ts +3 -2
- package/docs/api.md +17 -0
- package/docs/migrate.md +42 -0
- package/package.json +6 -4
- package/src/App/MagixEvent/index.ts +66 -0
- package/src/App/Storage/StorageEvent.ts +21 -0
- package/src/App/Storage/index.ts +284 -0
- package/src/App/Storage/typings.ts +21 -0
- package/src/App/Storage/utils.ts +17 -0
- package/src/AppContext.ts +61 -21
- package/src/AppListener.ts +15 -11
- package/src/AppManager.ts +141 -95
- package/src/AppProxy.ts +49 -52
- package/src/AttributesDelegate.ts +76 -49
- package/src/Base/Context.ts +2 -6
- package/src/Base/index.ts +2 -2
- package/src/BoxManager.ts +89 -31
- package/src/BuiltinApps.ts +24 -0
- package/src/ContainerResizeObserver.ts +62 -0
- package/src/Cursor/Cursor.ts +35 -39
- package/src/Cursor/index.ts +79 -43
- package/src/Helper.ts +30 -0
- package/src/ReconnectRefresher.ts +25 -10
- package/src/Utils/Common.ts +35 -13
- package/src/Utils/Reactive.ts +9 -3
- package/src/Utils/RoomHacker.ts +20 -5
- package/src/Utils/error.ts +6 -1
- package/src/{MainView.ts → View/MainView.ts} +19 -27
- package/src/View/ViewManager.ts +53 -0
- package/src/constants.ts +2 -3
- package/src/index.ts +144 -171
- package/src/shim.d.ts +4 -0
- package/src/style.css +7 -1
- package/src/typings.ts +3 -2
- package/vite.config.js +4 -1
- package/dist/Utils/CameraStore.d.ts +0 -15
- package/dist/ViewManager.d.ts +0 -29
- package/dist/sdk.d.ts +0 -14
- package/src/Utils/CameraStore.ts +0 -72
- package/src/sdk.ts +0 -39
- package/src/viewManager.ts +0 -177
package/src/BoxManager.ts
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
import {
|
1
|
+
import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
|
2
2
|
import { debounce, maxBy } from "lodash";
|
3
|
-
import { AppAttributes, DEFAULT_COLLECTOR_STYLE, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
|
4
3
|
import {
|
5
4
|
TELE_BOX_MANAGER_EVENT,
|
6
5
|
TELE_BOX_STATE,
|
7
6
|
TeleBoxCollector,
|
8
7
|
TeleBoxManager,
|
9
8
|
} from "@netless/telebox-insider";
|
10
|
-
import
|
9
|
+
import { emitter, WindowManager } from "./index";
|
10
|
+
import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
|
11
11
|
import type {
|
12
12
|
TeleBoxManagerUpdateConfig,
|
13
13
|
TeleBoxManagerCreateConfig,
|
14
14
|
ReadonlyTeleBox,
|
15
15
|
TeleBoxManagerConfig,
|
16
16
|
TeleBoxColorScheme,
|
17
|
+
TeleBoxRect,
|
17
18
|
} from "@netless/telebox-insider";
|
18
19
|
import type Emittery from "emittery";
|
19
|
-
import type { AppManager } from "./AppManager";
|
20
20
|
import type { NetlessApp } from "./typings";
|
21
21
|
import type { View } from "white-web-sdk";
|
22
22
|
|
@@ -48,31 +48,63 @@ export type CreateTeleBoxManagerConfig = {
|
|
48
48
|
prefersColorScheme?: TeleBoxColorScheme;
|
49
49
|
};
|
50
50
|
|
51
|
+
export type BoxManagerContext = {
|
52
|
+
safeSetAttributes: (attributes: any) => void;
|
53
|
+
getMainView: () => View;
|
54
|
+
updateAppState: (appId: string, field: AppAttributes, value: any) => void;
|
55
|
+
emitter: EmitterType;
|
56
|
+
callbacks: CallbacksType;
|
57
|
+
canOperate: () => boolean;
|
58
|
+
notifyContainerRectUpdate: (rect: TeleBoxRect) => void;
|
59
|
+
cleanFocus: () => void;
|
60
|
+
};
|
61
|
+
|
62
|
+
export const createBoxManager = (
|
63
|
+
manager: WindowManager,
|
64
|
+
callbacks: CallbacksType,
|
65
|
+
emitter: EmitterType,
|
66
|
+
options: CreateTeleBoxManagerConfig
|
67
|
+
) => {
|
68
|
+
return new BoxManager(
|
69
|
+
{
|
70
|
+
safeSetAttributes: (attributes: any) => manager.safeSetAttributes(attributes),
|
71
|
+
getMainView: () => manager.mainView,
|
72
|
+
updateAppState: (...args) => manager.appManager?.store.updateAppState(...args),
|
73
|
+
canOperate: () => manager.canOperate,
|
74
|
+
notifyContainerRectUpdate: (rect: TeleBoxRect) =>
|
75
|
+
manager.appManager?.notifyContainerRectUpdate(rect),
|
76
|
+
cleanFocus: () => manager.appManager?.store.cleanFocus(),
|
77
|
+
callbacks,
|
78
|
+
emitter,
|
79
|
+
},
|
80
|
+
options
|
81
|
+
);
|
82
|
+
};
|
83
|
+
|
51
84
|
export class BoxManager {
|
52
85
|
public teleBoxManager: TeleBoxManager;
|
53
|
-
public appBoxMap: Map<string, string> = new Map();
|
54
|
-
private mainView = this.manager.mainView;
|
55
86
|
|
56
87
|
constructor(
|
57
|
-
private
|
58
|
-
createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
|
88
|
+
private context: BoxManagerContext,
|
89
|
+
private createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
|
59
90
|
) {
|
91
|
+
const { emitter, callbacks } = context;
|
60
92
|
this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
|
61
93
|
this.teleBoxManager.events.on(TELE_BOX_MANAGER_EVENT.State, state => {
|
62
94
|
if (state) {
|
63
|
-
callbacks.emit("boxStateChange", state);
|
64
|
-
emitter.emit("boxStateChange", state);
|
95
|
+
this.context.callbacks.emit("boxStateChange", state);
|
96
|
+
this.context.emitter.emit("boxStateChange", state);
|
65
97
|
}
|
66
98
|
});
|
67
99
|
this.teleBoxManager.events.on("minimized", minimized => {
|
68
|
-
this.
|
100
|
+
this.context.safeSetAttributes({ minimized });
|
69
101
|
if (minimized) {
|
70
|
-
this.
|
102
|
+
this.context.cleanFocus();
|
71
103
|
this.blurAllBox();
|
72
104
|
}
|
73
105
|
});
|
74
106
|
this.teleBoxManager.events.on("maximized", maximized => {
|
75
|
-
this.
|
107
|
+
this.context.safeSetAttributes({ maximized });
|
76
108
|
});
|
77
109
|
this.teleBoxManager.events.on("removed", boxes => {
|
78
110
|
boxes.forEach(box => {
|
@@ -97,7 +129,7 @@ export class BoxManager {
|
|
97
129
|
);
|
98
130
|
this.teleBoxManager.events.on("focused", box => {
|
99
131
|
if (box) {
|
100
|
-
if (this.
|
132
|
+
if (this.canOperate) {
|
101
133
|
emitter.emit("focus", { appId: box.id });
|
102
134
|
} else {
|
103
135
|
this.teleBoxManager.blurBox(box.id);
|
@@ -111,8 +143,21 @@ export class BoxManager {
|
|
111
143
|
callbacks.emit("prefersColorSchemeChange", colorScheme);
|
112
144
|
});
|
113
145
|
this.teleBoxManager.events.on("z_index", box => {
|
114
|
-
this.
|
146
|
+
this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
|
115
147
|
});
|
148
|
+
emitter.on("playgroundSizeChange", this.playgroundSizeChangeListener);
|
149
|
+
}
|
150
|
+
|
151
|
+
private playgroundSizeChangeListener = () => {
|
152
|
+
this.updateManagerRect();
|
153
|
+
}
|
154
|
+
|
155
|
+
private get mainView() {
|
156
|
+
return this.context.getMainView();
|
157
|
+
}
|
158
|
+
|
159
|
+
private get canOperate() {
|
160
|
+
return this.context.canOperate();
|
116
161
|
}
|
117
162
|
|
118
163
|
public get boxState() {
|
@@ -135,6 +180,10 @@ export class BoxManager {
|
|
135
180
|
return this.teleBoxManager.prefersColorScheme;
|
136
181
|
}
|
137
182
|
|
183
|
+
public get boxSize() {
|
184
|
+
return this.teleBoxManager.boxes.length;
|
185
|
+
}
|
186
|
+
|
138
187
|
public createBox(params: CreateBoxParams): void {
|
139
188
|
if (!this.teleBoxManager) return;
|
140
189
|
let { minwidth = MIN_WIDTH, minheight = MIN_HEIGHT } = params.app.config ?? {};
|
@@ -159,14 +208,14 @@ export class BoxManager {
|
|
159
208
|
id: params.appId,
|
160
209
|
};
|
161
210
|
this.teleBoxManager.create(createBoxConfig, params.smartPosition);
|
162
|
-
emitter.emit(`${params.appId}${Events.WindowCreated}` as any);
|
211
|
+
this.context.emitter.emit(`${params.appId}${Events.WindowCreated}` as any);
|
163
212
|
}
|
164
213
|
|
165
214
|
public setBoxInitState(appId: string): void {
|
166
215
|
const box = this.teleBoxManager.queryOne({ id: appId });
|
167
216
|
if (box) {
|
168
217
|
if (box.state === TELE_BOX_STATE.Maximized) {
|
169
|
-
emitter.emit("resize", {
|
218
|
+
this.context.emitter.emit("resize", {
|
170
219
|
appId: appId,
|
171
220
|
x: box.x,
|
172
221
|
y: box.y,
|
@@ -193,24 +242,26 @@ export class BoxManager {
|
|
193
242
|
fence: false,
|
194
243
|
prefersColorScheme: createTeleBoxManagerConfig?.prefersColorScheme,
|
195
244
|
};
|
196
|
-
|
197
|
-
const styles = {
|
198
|
-
...DEFAULT_COLLECTOR_STYLE,
|
199
|
-
...createTeleBoxManagerConfig?.collectorStyles,
|
200
|
-
};
|
201
|
-
const teleBoxCollector = new TeleBoxCollector({
|
202
|
-
styles: styles,
|
203
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
204
|
-
}).mount(container!);
|
205
|
-
initManagerState.collector = teleBoxCollector;
|
245
|
+
|
206
246
|
const manager = new TeleBoxManager(initManagerState);
|
207
247
|
if (this.teleBoxManager) {
|
208
248
|
this.teleBoxManager.destroy();
|
209
249
|
}
|
210
250
|
this.teleBoxManager = manager;
|
251
|
+
const container = createTeleBoxManagerConfig?.collectorContainer || WindowManager.wrapper;
|
252
|
+
if (container) {
|
253
|
+
this.setCollectorContainer(container);
|
254
|
+
}
|
211
255
|
return manager;
|
212
256
|
}
|
213
257
|
|
258
|
+
public setCollectorContainer(container: HTMLElement) {
|
259
|
+
const collector = new TeleBoxCollector({
|
260
|
+
styles: this.createTeleBoxManagerConfig?.collectorStyles,
|
261
|
+
}).mount(container);
|
262
|
+
this.teleBoxManager.setCollector(collector);
|
263
|
+
}
|
264
|
+
|
214
265
|
public getBox(appId: string): ReadonlyTeleBox | undefined {
|
215
266
|
return this.teleBoxManager.queryOne({ id: appId });
|
216
267
|
}
|
@@ -251,7 +302,7 @@ export class BoxManager {
|
|
251
302
|
);
|
252
303
|
setTimeout(() => {
|
253
304
|
if (state.focus) {
|
254
|
-
this.teleBoxManager.focusBox(box.id, true)
|
305
|
+
this.teleBoxManager.focusBox(box.id, true);
|
255
306
|
}
|
256
307
|
if (state.maximized != null) {
|
257
308
|
this.teleBoxManager.setMaximized(Boolean(state.maximized), true);
|
@@ -260,7 +311,7 @@ export class BoxManager {
|
|
260
311
|
this.teleBoxManager.setMinimized(Boolean(state.minimized), true);
|
261
312
|
}
|
262
313
|
}, 50);
|
263
|
-
callbacks.emit("boxStateChange", this.teleBoxManager.state);
|
314
|
+
this.context.callbacks.emit("boxStateChange", this.teleBoxManager.state);
|
264
315
|
}
|
265
316
|
}
|
266
317
|
|
@@ -269,7 +320,7 @@ export class BoxManager {
|
|
269
320
|
if (rect && rect.width > 0 && rect.height > 0) {
|
270
321
|
const containerRect = { x: 0, y: 0, width: rect.width, height: rect.height };
|
271
322
|
this.teleBoxManager.setContainerRect(containerRect);
|
272
|
-
this.
|
323
|
+
this.context.notifyContainerRectUpdate(this.teleBoxManager.containerRect);
|
273
324
|
}
|
274
325
|
}
|
275
326
|
|
@@ -309,7 +360,9 @@ export class BoxManager {
|
|
309
360
|
}
|
310
361
|
|
311
362
|
public setMaximized(maximized: boolean) {
|
312
|
-
this.
|
363
|
+
if (maximized !== this.maximized) {
|
364
|
+
this.teleBoxManager.setMaximized(maximized, true);
|
365
|
+
}
|
313
366
|
}
|
314
367
|
|
315
368
|
public setMinimized(minimized: boolean, skipUpdate = true) {
|
@@ -334,7 +387,12 @@ export class BoxManager {
|
|
334
387
|
this.teleBoxManager.setPrefersColorScheme(colorScheme);
|
335
388
|
}
|
336
389
|
|
390
|
+
public setZIndex(id: string, zIndex: number, skipUpdate = true) {
|
391
|
+
this.teleBoxManager.update(id, { zIndex }, skipUpdate);
|
392
|
+
}
|
393
|
+
|
337
394
|
public destroy() {
|
395
|
+
emitter.off("playgroundSizeChange", this.playgroundSizeChangeListener);
|
338
396
|
this.teleBoxManager.destroy();
|
339
397
|
}
|
340
398
|
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import AppDocsViewer from "@netless/app-docs-viewer";
|
2
|
+
import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
|
3
|
+
import { WindowManager } from "./index";
|
4
|
+
import "@netless/app-docs-viewer/dist/style.css";
|
5
|
+
|
6
|
+
export const setupBuiltin = () => {
|
7
|
+
if (WindowManager.debug) {
|
8
|
+
setOptions({ verbose: true });
|
9
|
+
}
|
10
|
+
|
11
|
+
WindowManager.register({
|
12
|
+
kind: AppDocsViewer.kind,
|
13
|
+
src: AppDocsViewer,
|
14
|
+
});
|
15
|
+
WindowManager.register({
|
16
|
+
kind: AppMediaPlayer.kind,
|
17
|
+
src: AppMediaPlayer,
|
18
|
+
});
|
19
|
+
};
|
20
|
+
|
21
|
+
export const BuiltinApps = {
|
22
|
+
DocsViewer: AppDocsViewer.kind as string,
|
23
|
+
MediaPlayer: AppMediaPlayer.kind as string,
|
24
|
+
};
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
|
2
|
+
import { WindowManager } from "./index";
|
3
|
+
import type { EmitterType} from "./index";
|
4
|
+
|
5
|
+
const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
|
6
|
+
|
7
|
+
export class ContainerResizeObserver {
|
8
|
+
private containerResizeObserver?: ResizeObserver;
|
9
|
+
|
10
|
+
constructor(private emitter: EmitterType) {}
|
11
|
+
|
12
|
+
public static create(
|
13
|
+
container: HTMLElement,
|
14
|
+
sizer: HTMLElement,
|
15
|
+
wrapper: HTMLDivElement,
|
16
|
+
emitter: EmitterType,
|
17
|
+
) {
|
18
|
+
const containerResizeObserver = new ContainerResizeObserver(emitter);
|
19
|
+
containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
|
20
|
+
return containerResizeObserver;
|
21
|
+
}
|
22
|
+
|
23
|
+
public observePlaygroundSize(
|
24
|
+
container: HTMLElement,
|
25
|
+
sizer: HTMLElement,
|
26
|
+
wrapper: HTMLDivElement
|
27
|
+
) {
|
28
|
+
this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
|
29
|
+
|
30
|
+
this.containerResizeObserver = new ResizeObserver(entries => {
|
31
|
+
const containerRect = entries[0]?.contentRect;
|
32
|
+
if (containerRect) {
|
33
|
+
this.updateSizer(containerRect, sizer, wrapper);
|
34
|
+
this.emitter.emit("playgroundSizeChange", containerRect)
|
35
|
+
}
|
36
|
+
});
|
37
|
+
|
38
|
+
this.containerResizeObserver.observe(container);
|
39
|
+
}
|
40
|
+
|
41
|
+
private updateSizer(
|
42
|
+
{ width, height }: DOMRectReadOnly,
|
43
|
+
sizer: HTMLElement,
|
44
|
+
wrapper: HTMLDivElement
|
45
|
+
) {
|
46
|
+
if (width && height) {
|
47
|
+
if (height / width > WindowManager.containerSizeRatio) {
|
48
|
+
height = width * WindowManager.containerSizeRatio;
|
49
|
+
sizer.classList.toggle("netless-window-manager-sizer-horizontal", true);
|
50
|
+
} else {
|
51
|
+
width = height / WindowManager.containerSizeRatio;
|
52
|
+
sizer.classList.toggle("netless-window-manager-sizer-horizontal", false);
|
53
|
+
}
|
54
|
+
wrapper.style.width = `${width}px`;
|
55
|
+
wrapper.style.height = `${height}px`;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
public disconnect() {
|
60
|
+
this.containerResizeObserver?.disconnect();
|
61
|
+
}
|
62
|
+
}
|
package/src/Cursor/Cursor.ts
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import App from './Cursor.svelte';
|
2
|
-
import pRetry from 'p-retry';
|
3
2
|
import { ApplianceMap } from './icons';
|
4
|
-
import { ApplianceNames
|
3
|
+
import { ApplianceNames } from 'white-web-sdk';
|
5
4
|
import { CursorState } from '../constants';
|
6
5
|
import { Fields } from '../AttributesDelegate';
|
7
6
|
import { get, omit } from 'lodash';
|
@@ -19,64 +18,61 @@ export type Payload = {
|
|
19
18
|
|
20
19
|
export class Cursor extends Base {
|
21
20
|
private member?: RoomMember;
|
22
|
-
private disposer: any;
|
23
21
|
private timer?: number;
|
24
22
|
private component?: SvelteComponent;
|
25
23
|
|
26
24
|
constructor(
|
27
25
|
manager: AppManager,
|
26
|
+
addCursorChangeListener: (uid: string, callback: (position: Position, state: CursorState) => void) => void,
|
28
27
|
private cursors: any,
|
29
28
|
private memberId: string,
|
30
29
|
private cursorManager: CursorManager,
|
31
|
-
private wrapper?: HTMLElement
|
30
|
+
private wrapper?: HTMLElement,
|
32
31
|
) {
|
33
32
|
super(manager);
|
34
33
|
this.setMember();
|
35
34
|
this.createCursor();
|
36
|
-
|
37
|
-
this.disposer && this.disposer();
|
38
|
-
if (!this.cursorPosition) {
|
39
|
-
console.warn(`${memberId} not exist`);
|
40
|
-
}
|
41
|
-
this.startReaction();
|
42
|
-
}, { retries: 3 });
|
35
|
+
addCursorChangeListener(this.memberId, this.onCursorChange);
|
43
36
|
this.autoHidden();
|
44
37
|
}
|
45
38
|
|
46
|
-
private
|
47
|
-
|
48
|
-
const
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
const rect = this.cursorManager.wrapperRect;
|
53
|
-
if (this.component && rect) {
|
54
|
-
this.autoHidden();
|
55
|
-
this.moveCursor(cursor, rect, this.manager.mainView);
|
56
|
-
}
|
57
|
-
} else {
|
58
|
-
const focusView = this.cursorManager.focusView;
|
59
|
-
const viewRect = focusView?.divElement?.getBoundingClientRect();
|
60
|
-
const viewCamera = focusView?.camera;
|
61
|
-
if (focusView && viewRect && viewCamera && this.component) {
|
62
|
-
this.autoHidden();
|
63
|
-
this.moveCursor(cursor, viewRect, focusView);
|
64
|
-
}
|
39
|
+
private onCursorChange = (position: Position, state: CursorState) => {
|
40
|
+
if (position.type === "main") {
|
41
|
+
const rect = this.cursorManager.wrapperRect;
|
42
|
+
if (this.component && rect) {
|
43
|
+
this.autoHidden();
|
44
|
+
this.moveCursor(position, rect, this.manager.mainView);
|
65
45
|
}
|
66
|
-
|
67
|
-
|
46
|
+
} else {
|
47
|
+
const focusView = this.cursorManager.focusView;
|
48
|
+
// TODO 可以存一个当前 focusView 的 Rect 这样只有 focus 切换的时候才调用 getBoundingClientRect
|
49
|
+
const viewRect = focusView?.divElement?.getBoundingClientRect();
|
50
|
+
const viewCamera = focusView?.camera;
|
51
|
+
if (focusView && viewRect && viewCamera && this.component) {
|
52
|
+
this.autoHidden();
|
53
|
+
this.moveCursor(position, viewRect, focusView);
|
68
54
|
}
|
69
|
-
}
|
55
|
+
}
|
56
|
+
if (state && state === CursorState.Leave) {
|
57
|
+
this.hide();
|
58
|
+
}
|
70
59
|
}
|
71
60
|
|
72
61
|
private moveCursor(cursor: Position, rect: DOMRect, view: any) {
|
73
|
-
const { x, y } = cursor;
|
62
|
+
const { x, y, type } = cursor;
|
74
63
|
const point = view?.screen.convertPointToScreen(x, y);
|
75
64
|
if (point) {
|
76
|
-
|
77
|
-
|
65
|
+
let translateX = point.x - 2;
|
66
|
+
let translateY = point.y - 18;
|
67
|
+
if (type === "app") {
|
68
|
+
const wrapperRect = this.cursorManager.wrapperRect;
|
69
|
+
if (wrapperRect) {
|
70
|
+
translateX = translateX + rect.x - wrapperRect.x;
|
71
|
+
translateY = translateY + rect.y - wrapperRect.y;
|
72
|
+
}
|
73
|
+
}
|
78
74
|
if (point.x < 0 || point.x > rect.width || point.y < 0 || point.y > rect.height) {
|
79
|
-
this.component?.$set({ visible: false });
|
75
|
+
this.component?.$set({ visible: false, x: translateX, y: translateY });
|
80
76
|
} else {
|
81
77
|
this.component?.$set({ visible: true, x: translateX, y: translateY });
|
82
78
|
}
|
@@ -149,7 +145,7 @@ export class Cursor extends Base {
|
|
149
145
|
private async createCursor() {
|
150
146
|
if (this.member && this.wrapper) {
|
151
147
|
this.component = new App({
|
152
|
-
target:
|
148
|
+
target: this.wrapper,
|
153
149
|
props: this.initProps(),
|
154
150
|
});
|
155
151
|
}
|
@@ -189,10 +185,10 @@ export class Cursor extends Base {
|
|
189
185
|
}
|
190
186
|
|
191
187
|
public destroy() {
|
192
|
-
this.disposer && this.disposer();
|
193
188
|
if (this.component) {
|
194
189
|
this.component.$destroy();
|
195
190
|
}
|
191
|
+
this.manager.refresher?.remove(this.memberId);
|
196
192
|
this.cursorManager.cursorInstances.delete(this.memberId);
|
197
193
|
}
|
198
194
|
|
package/src/Cursor/index.ts
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import {
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
import {
|
7
|
-
import {
|
8
|
-
import
|
1
|
+
import { autorun } from "white-web-sdk";
|
2
|
+
import { Base } from "../Base";
|
3
|
+
import { compact, debounce, get, uniq } from "lodash";
|
4
|
+
import { Cursor } from "./Cursor";
|
5
|
+
import { CursorState } from "../constants";
|
6
|
+
import { emitter, WindowManager } from "../index";
|
7
|
+
import { Fields } from "../AttributesDelegate";
|
8
|
+
import { onObjectInserted } from "../Utils/Reactive";
|
9
|
+
import { SideEffectManager } from "side-effect-manager";
|
10
|
+
import type { PositionType, Position } from "../AttributesDelegate";
|
9
11
|
import type { Point, RoomMember, View } from "white-web-sdk";
|
10
12
|
import type { AppManager } from "../AppManager";
|
11
13
|
|
@@ -18,28 +20,45 @@ export type MoveCursorParams = {
|
|
18
20
|
uid: string;
|
19
21
|
x: number;
|
20
22
|
y: number;
|
21
|
-
}
|
23
|
+
};
|
22
24
|
export class CursorManager extends Base {
|
23
25
|
public containerRect?: DOMRect;
|
24
26
|
public wrapperRect?: DOMRect;
|
25
27
|
public cursorInstances: Map<string, Cursor> = new Map();
|
26
28
|
public roomMembers?: readonly RoomMember[];
|
27
29
|
private mainViewElement?: HTMLDivElement;
|
30
|
+
private sideEffectManager = new SideEffectManager();
|
28
31
|
|
29
32
|
constructor(private appManager: AppManager) {
|
30
33
|
super(appManager);
|
31
34
|
this.roomMembers = this.appManager.room?.state.roomMembers;
|
32
35
|
const wrapper = WindowManager.wrapper;
|
33
36
|
if (wrapper) {
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
this.setupWrapper(wrapper);
|
38
|
+
}
|
39
|
+
emitter.on("onReconnected", () => {
|
40
|
+
this.onReconnect();
|
41
|
+
});
|
42
|
+
}
|
43
|
+
|
44
|
+
public setupWrapper(wrapper: HTMLElement) {
|
45
|
+
if (this.manager.refresher?.hasReactor("cursors")) {
|
46
|
+
this.destroy();
|
42
47
|
}
|
48
|
+
this.sideEffectManager.add(() => {
|
49
|
+
wrapper.addEventListener("pointerenter", this.mouseMoveListener);
|
50
|
+
wrapper.addEventListener("pointermove", this.mouseMoveListener);
|
51
|
+
wrapper.addEventListener("pointerleave", this.mouseLeaveListener);
|
52
|
+
return () => {
|
53
|
+
wrapper.removeEventListener("pointerenter", this.mouseMoveListener);
|
54
|
+
wrapper.removeEventListener("pointermove", this.mouseMoveListener);
|
55
|
+
wrapper.removeEventListener("pointerleave", this.mouseLeaveListener);
|
56
|
+
};
|
57
|
+
});
|
58
|
+
|
59
|
+
this.initCursorAttributes();
|
60
|
+
this.wrapperRect = wrapper.getBoundingClientRect();
|
61
|
+
this.startReaction(wrapper);
|
43
62
|
}
|
44
63
|
|
45
64
|
public setMainViewDivElement(div: HTMLDivElement) {
|
@@ -51,27 +70,25 @@ export class CursorManager extends Base {
|
|
51
70
|
return onObjectInserted(this.cursors, () => {
|
52
71
|
this.handleRoomMembersChange(wrapper);
|
53
72
|
});
|
54
|
-
})
|
73
|
+
});
|
55
74
|
}
|
56
75
|
|
57
76
|
private getUids = (members: readonly RoomMember[] | undefined) => {
|
58
77
|
return compact(uniq(members?.map(member => member.payload?.uid)));
|
59
|
-
}
|
78
|
+
};
|
60
79
|
|
61
80
|
private handleRoomMembersChange = debounce((wrapper: HTMLElement) => {
|
62
81
|
const uids = this.getUids(this.roomMembers);
|
63
82
|
const cursors = Object.keys(this.cursors);
|
64
83
|
if (uids?.length) {
|
65
84
|
cursors.map(uid => {
|
66
|
-
if (
|
67
|
-
uids.includes(uid) &&
|
68
|
-
!this.cursorInstances.has(uid)
|
69
|
-
) {
|
85
|
+
if (uids.includes(uid) && !this.cursorInstances.has(uid)) {
|
70
86
|
if (uid === this.context.uid) {
|
71
87
|
return;
|
72
88
|
}
|
73
89
|
const component = new Cursor(
|
74
90
|
this.appManager,
|
91
|
+
this.addCursorChangeListener,
|
75
92
|
this.cursors,
|
76
93
|
uid,
|
77
94
|
this,
|
@@ -79,7 +96,7 @@ export class CursorManager extends Base {
|
|
79
96
|
);
|
80
97
|
this.cursorInstances.set(uid, component);
|
81
98
|
}
|
82
|
-
})
|
99
|
+
});
|
83
100
|
}
|
84
101
|
}, 100);
|
85
102
|
|
@@ -99,13 +116,6 @@ export class CursorManager extends Base {
|
|
99
116
|
this.updateCursor(this.getType(event), event.clientX, event.clientY);
|
100
117
|
}, 5);
|
101
118
|
|
102
|
-
private touchMoveListener = debounce((event: TouchEvent) => {
|
103
|
-
if (event.touches.length === 1) {
|
104
|
-
const touchEvent = event.touches[0];
|
105
|
-
this.updateCursor(this.getType(touchEvent), touchEvent.clientX, touchEvent.clientY);
|
106
|
-
}
|
107
|
-
}, 5);
|
108
|
-
|
109
119
|
private updateCursor(event: EventType, clientX: number, clientY: number) {
|
110
120
|
if (this.wrapperRect && this.manager.canOperate) {
|
111
121
|
const view = event.type === "main" ? this.appManager.mainView : this.focusView;
|
@@ -121,7 +131,11 @@ export class CursorManager extends Base {
|
|
121
131
|
}
|
122
132
|
}
|
123
133
|
|
124
|
-
private getPoint = (
|
134
|
+
private getPoint = (
|
135
|
+
view: View | undefined,
|
136
|
+
clientX: number,
|
137
|
+
clientY: number
|
138
|
+
): Point | undefined => {
|
125
139
|
const rect = view?.divElement?.getBoundingClientRect();
|
126
140
|
if (rect) {
|
127
141
|
const point = view?.convertToPointInWorld({
|
@@ -130,7 +144,7 @@ export class CursorManager extends Base {
|
|
130
144
|
});
|
131
145
|
return point;
|
132
146
|
}
|
133
|
-
}
|
147
|
+
};
|
134
148
|
|
135
149
|
/**
|
136
150
|
* 因为窗口内框在不同分辨率下的大小不一样,所以这里通过来鼠标事件的 target 来判断是在主白板还是在 APP 中
|
@@ -140,7 +154,7 @@ export class CursorManager extends Base {
|
|
140
154
|
const focusApp = this.appManager.focusApp;
|
141
155
|
switch (target.parentElement) {
|
142
156
|
case this.mainViewElement: {
|
143
|
-
return { type: "main" };
|
157
|
+
return { type: "main" };
|
144
158
|
}
|
145
159
|
case focusApp?.view?.divElement: {
|
146
160
|
return { type: "app" };
|
@@ -217,19 +231,41 @@ export class CursorManager extends Base {
|
|
217
231
|
});
|
218
232
|
}
|
219
233
|
|
220
|
-
public
|
221
|
-
const wrapper = WindowManager.wrapper;
|
222
|
-
if (wrapper) {
|
223
|
-
wrapper.removeEventListener("mousemove", this.mouseMoveListener);
|
224
|
-
wrapper.removeEventListener("touchstart", this.touchMoveListener);
|
225
|
-
wrapper.removeEventListener("touchmove", this.touchMoveListener);
|
226
|
-
wrapper.removeEventListener("mouseleave", this.mouseLeaveListener);
|
227
|
-
wrapper.removeEventListener("touchend", this.mouseLeaveListener);
|
228
|
-
}
|
234
|
+
public onReconnect() {
|
229
235
|
if (this.cursorInstances.size) {
|
230
236
|
this.cursorInstances.forEach(cursor => cursor.destroy());
|
231
237
|
this.cursorInstances.clear();
|
232
238
|
}
|
239
|
+
this.roomMembers = this.appManager.room?.state.roomMembers;
|
240
|
+
if (WindowManager.wrapper) {
|
241
|
+
this.handleRoomMembersChange(WindowManager.wrapper);
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
public addCursorChangeListener = (
|
246
|
+
uid: string,
|
247
|
+
callback: (position: Position, state: CursorState) => void
|
248
|
+
) => {
|
249
|
+
this.manager.refresher?.add(uid, () => {
|
250
|
+
const disposer = autorun(() => {
|
251
|
+
const position = get(this.cursors, [uid, Fields.Position]);
|
252
|
+
const state = get(this.cursors, [uid, Fields.CursorState]);
|
253
|
+
if (position) {
|
254
|
+
callback(position, state);
|
255
|
+
}
|
256
|
+
});
|
257
|
+
return disposer;
|
258
|
+
});
|
259
|
+
};
|
260
|
+
|
261
|
+
public destroy() {
|
262
|
+
this.sideEffectManager.flushAll();
|
263
|
+
if (this.cursorInstances.size) {
|
264
|
+
this.cursorInstances.forEach(cursor => {
|
265
|
+
cursor.destroy();
|
266
|
+
});
|
267
|
+
this.cursorInstances.clear();
|
268
|
+
}
|
233
269
|
this.manager.refresher?.remove("cursors");
|
234
270
|
}
|
235
271
|
}
|