@netless/window-manager 0.4.0-canary.9 → 0.4.2

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.
Files changed (74) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/.idea/window-manager.iml +12 -0
  5. package/.vscode/settings.json +1 -0
  6. package/CHANGELOG.md +43 -2
  7. package/README.md +3 -0
  8. package/dist/App/MagixEvent/index.d.ts +29 -0
  9. package/dist/App/Storage/index.d.ts +19 -6
  10. package/dist/App/Storage/typings.d.ts +1 -0
  11. package/dist/AppContext.d.ts +39 -17
  12. package/dist/AppListener.d.ts +2 -0
  13. package/dist/AppManager.d.ts +22 -8
  14. package/dist/AppProxy.d.ts +5 -5
  15. package/dist/AttributesDelegate.d.ts +2 -2
  16. package/dist/BoxManager.d.ts +6 -4
  17. package/dist/BuiltinApps.d.ts +0 -1
  18. package/dist/Cursor/Cursor.d.ts +10 -12
  19. package/dist/Cursor/index.d.ts +6 -16
  20. package/dist/Helper.d.ts +1 -0
  21. package/dist/Register/index.d.ts +5 -0
  22. package/dist/Register/storage.d.ts +5 -1
  23. package/dist/Utils/AppCreateQueue.d.ts +11 -0
  24. package/dist/Utils/Common.d.ts +4 -1
  25. package/dist/Utils/RoomHacker.d.ts +3 -3
  26. package/dist/View/MainView.d.ts +4 -3
  27. package/dist/constants.d.ts +5 -2
  28. package/dist/index.d.ts +32 -6
  29. package/dist/index.es.js +41 -1
  30. package/dist/index.es.js.map +1 -1
  31. package/dist/index.umd.js +41 -1
  32. package/dist/index.umd.js.map +1 -1
  33. package/dist/style.css +1 -1
  34. package/dist/typings.d.ts +2 -2
  35. package/docs/advanced.md +53 -0
  36. package/docs/api.md +79 -6
  37. package/docs/concept.md +9 -0
  38. package/docs/replay.md +40 -0
  39. package/package.json +8 -9
  40. package/src/App/MagixEvent/index.ts +68 -0
  41. package/src/App/Storage/index.ts +89 -43
  42. package/src/App/Storage/typings.ts +4 -2
  43. package/src/AppContext.ts +61 -24
  44. package/src/AppListener.ts +27 -8
  45. package/src/AppManager.ts +231 -70
  46. package/src/AppProxy.ts +40 -29
  47. package/src/AttributesDelegate.ts +2 -2
  48. package/src/BoxManager.ts +33 -19
  49. package/src/BuiltinApps.ts +0 -1
  50. package/src/ContainerResizeObserver.ts +3 -3
  51. package/src/Cursor/Cursor.svelte +25 -21
  52. package/src/Cursor/Cursor.ts +25 -38
  53. package/src/Cursor/icons.ts +2 -0
  54. package/src/Cursor/index.ts +45 -139
  55. package/src/Helper.ts +12 -1
  56. package/src/Register/index.ts +32 -17
  57. package/src/Register/loader.ts +28 -13
  58. package/src/Register/storage.ts +6 -1
  59. package/src/Utils/AppCreateQueue.ts +54 -0
  60. package/src/Utils/Common.ts +35 -2
  61. package/src/Utils/RoomHacker.ts +33 -18
  62. package/src/View/MainView.ts +19 -12
  63. package/src/View/ViewManager.ts +1 -2
  64. package/src/constants.ts +6 -2
  65. package/src/image/laser-pointer-cursor.svg +17 -0
  66. package/src/index.ts +150 -33
  67. package/src/shim.d.ts +2 -1
  68. package/src/style.css +6 -1
  69. package/src/typings.ts +2 -2
  70. package/vite.config.js +7 -4
  71. package/dist/Base/Context.d.ts +0 -12
  72. package/dist/Base/index.d.ts +0 -7
  73. package/src/Base/Context.ts +0 -45
  74. package/src/Base/index.ts +0 -10
package/src/AppProxy.ts CHANGED
@@ -3,15 +3,12 @@ import { AppAttributes, AppEvents, Events } from "./constants";
3
3
  import { AppContext } from "./AppContext";
4
4
  import { appRegister } from "./Register";
5
5
  import { autorun } from "white-web-sdk";
6
+ import { BoxManagerNotFoundError } from "./Utils/error";
7
+ import { debounce, get } from "lodash";
6
8
  import { emitter } from "./index";
7
9
  import { Fields } from "./AttributesDelegate";
8
- import { get } from "lodash";
10
+ import { entireScenes, getScenePath, removeScenes, setScenePath, setViewFocusScenePath } from "./Utils/Common";
9
11
  import { log } from "./Utils/log";
10
- import {
11
- setScenePath,
12
- setViewFocusScenePath,
13
- getScenePath
14
- } from "./Utils/Common";
15
12
  import type {
16
13
  AppEmitterEvent,
17
14
  AppInitState,
@@ -23,10 +20,9 @@ import type { SceneState, View, SceneDefinition } from "white-web-sdk";
23
20
  import type { AppManager } from "./AppManager";
24
21
  import type { NetlessApp } from "./typings";
25
22
  import type { ReadonlyTeleBox } from "@netless/telebox-insider";
26
- import { Base } from "./Base";
27
- import { BoxManagerNotFoundError } from "./Utils/error";
28
23
 
29
- export class AppProxy extends Base {
24
+ export class AppProxy {
25
+ public kind: string;
30
26
  public id: string;
31
27
  public scenePath?: string;
32
28
  public appEmitter: Emittery<AppEmitterEvent>;
@@ -36,7 +32,8 @@ export class AppProxy extends Base {
36
32
  private boxManager = this.manager.boxManager;
37
33
  private appProxies = this.manager.appProxies;
38
34
  private viewManager = this.manager.viewManager;
39
- private kind: string;
35
+ private store = this.manager.store;
36
+
40
37
  public isAddApp: boolean;
41
38
  private status: "normal" | "destroyed" = "normal";
42
39
  private stateKey: string;
@@ -45,11 +42,10 @@ export class AppProxy extends Base {
45
42
 
46
43
  constructor(
47
44
  private params: BaseInsertParams,
48
- manager: AppManager,
45
+ private manager: AppManager,
49
46
  appId: string,
50
47
  isAddApp: boolean
51
48
  ) {
52
- super(manager);
53
49
  this.kind = params.kind;
54
50
  this.id = appId;
55
51
  this.stateKey = `${this.id}_state`;
@@ -71,7 +67,7 @@ export class AppProxy extends Base {
71
67
  if (options) {
72
68
  this.scenePath = options.scenePath;
73
69
  if (this.appAttributes?.isDynamicPPT && this.scenePath) {
74
- this.scenes = this.manager.displayer.entireScenes()[this.scenePath];
70
+ this.scenes = entireScenes(this.manager.displayer)[this.scenePath];
75
71
  } else {
76
72
  this.scenes = options.scenes;
77
73
  }
@@ -113,9 +109,7 @@ export class AppProxy extends Base {
113
109
  this.manager.safeUpdateAttributes(["apps", this.id, Fields.FullPath], path);
114
110
  }
115
111
 
116
- public async baseInsertApp(
117
- skipUpdate = false,
118
- ): Promise<{ appId: string; app: NetlessApp }> {
112
+ public async baseInsertApp(skipUpdate = false): Promise<{ appId: string; app: NetlessApp }> {
119
113
  const params = this.params;
120
114
  if (!params.kind) {
121
115
  throw new Error("[WindowManager]: kind require");
@@ -123,30 +117,27 @@ export class AppProxy extends Base {
123
117
  const appImpl = await appRegister.appClasses.get(params.kind)?.();
124
118
  const appParams = appRegister.registered.get(params.kind);
125
119
  if (appImpl) {
126
- await this.setupApp(this.id, skipUpdate, appImpl, params.options, appParams?.appOptions);
120
+ await this.setupApp(
121
+ this.id,
122
+ skipUpdate,
123
+ appImpl,
124
+ params.options,
125
+ appParams?.appOptions
126
+ );
127
127
  } else {
128
128
  throw new Error(`[WindowManager]: app load failed ${params.kind} ${params.src}`);
129
129
  }
130
- this.context.updateManagerRect();
130
+ this.boxManager?.updateManagerRect();
131
131
  return {
132
132
  appId: this.id,
133
133
  app: appImpl,
134
134
  };
135
135
  }
136
136
 
137
- private focusApp() {
138
- this.focusBox();
139
- this.store.setMainViewFocusPath(this.manager.mainView);
140
- }
141
-
142
137
  public get box(): ReadonlyTeleBox | undefined {
143
138
  return this.boxManager?.getBox(this.id);
144
139
  }
145
140
 
146
- public focusBox() {
147
- this.boxManager?.focusBox({ appId: this.id });
148
- }
149
-
150
141
  private async setupApp(
151
142
  appId: string,
152
143
  skipUpdate: boolean,
@@ -174,7 +165,7 @@ export class AppProxy extends Base {
174
165
  // 延迟执行 setup, 防止初始化的属性没有更新成功
175
166
  const result = await app.setup(context);
176
167
  this.appResult = result;
177
- appRegister.notifyApp(app.kind, "created", { appId, result });
168
+ appRegister.notifyApp(this.kind, "created", { appId, result });
178
169
  this.afterSetupApp(boxInitState);
179
170
  this.fixMobileSize();
180
171
  }, 50);
@@ -186,6 +177,10 @@ export class AppProxy extends Base {
186
177
  canOperate: this.manager.canOperate,
187
178
  smartPosition: this.isAddApp,
188
179
  });
180
+ if (this.isAddApp && this.box) {
181
+ this.store.updateAppState(appId, AppAttributes.ZIndex, this.box.zIndex);
182
+ this.boxManager.focusBox({ appId }, false);
183
+ }
189
184
  } catch (error: any) {
190
185
  console.error(error);
191
186
  throw new Error(`[WindowManager]: app setup error: ${error.message}`);
@@ -317,7 +312,7 @@ export class AppProxy extends Base {
317
312
  }
318
313
  });
319
314
  });
320
- this.manager.refresher?.add(this.stateKey,() => {
315
+ this.manager.refresher?.add(this.stateKey, () => {
321
316
  return autorun(() => {
322
317
  const appState = this.appAttributes?.state;
323
318
  if (appState?.zIndex > 0 && appState.zIndex !== this.box?.zIndex) {
@@ -325,8 +320,20 @@ export class AppProxy extends Base {
325
320
  }
326
321
  });
327
322
  });
323
+ this.manager.refresher?.add(`${appId}-fullPath`, () => {
324
+ return autorun(() => {
325
+ const fullPath = this.appAttributes?.fullPath;
326
+ this.setFocusScenePathHandler(fullPath);
327
+ });
328
+ });
328
329
  };
329
330
 
331
+ private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
332
+ if (this.view && fullPath && fullPath !== this.view?.focusScenePath) {
333
+ setViewFocusScenePath(this.view, fullPath);
334
+ }
335
+ }, 50);
336
+
330
337
  public setScenePath(): void {
331
338
  if (!this.manager.canOperate) return;
332
339
  const fullScenePath = this.getFullScenePath();
@@ -365,6 +372,9 @@ export class AppProxy extends Base {
365
372
  }
366
373
  if (cleanAttrs) {
367
374
  this.store.cleanAppAttributes(this.id);
375
+ if (this.scenePath) {
376
+ removeScenes(this.manager.room, this.scenePath);
377
+ }
368
378
  }
369
379
  this.appProxies.delete(this.id);
370
380
 
@@ -372,6 +382,7 @@ export class AppProxy extends Base {
372
382
  this.manager.appStatus.delete(this.id);
373
383
  this.manager.refresher?.remove(this.id);
374
384
  this.manager.refresher?.remove(this.stateKey);
385
+ this.manager.refresher?.remove(`${this.id}-fullPath`);
375
386
  }
376
387
 
377
388
  public close(): Promise<void> {
@@ -123,7 +123,7 @@ export class AttributesDelegate {
123
123
  return this.getAppAttributes(id)?.options?.scenePath;
124
124
  }
125
125
 
126
- public getMainViewScenePath() {
126
+ public getMainViewScenePath(): string | undefined {
127
127
  return this.attributes["_mainScenePath"];
128
128
  }
129
129
 
@@ -159,7 +159,7 @@ export class AttributesDelegate {
159
159
  this.context.safeSetAttributes({ [Fields.MainViewSize]: { ...size } });
160
160
  }
161
161
 
162
- public setAppFocus(appId: string, focus: boolean) {
162
+ public setAppFocus = (appId: string, focus: boolean) => {
163
163
  if (focus) {
164
164
  this.context.safeSetAttributes({ [Fields.Focus]: appId });
165
165
  } else {
package/src/BoxManager.ts CHANGED
@@ -1,12 +1,11 @@
1
1
  import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
- import { debounce, maxBy } from "lodash";
2
+ import { debounce } from "lodash";
3
+ import { emitter, WindowManager } from "./index";
3
4
  import {
4
- TELE_BOX_MANAGER_EVENT,
5
5
  TELE_BOX_STATE,
6
6
  TeleBoxCollector,
7
7
  TeleBoxManager,
8
8
  } from "@netless/telebox-insider";
9
- import { emitter, WindowManager } from "./index";
10
9
  import type { AddAppOptions, AppInitState, EmitterType, CallbacksType } from "./index";
11
10
  import type {
12
11
  TeleBoxManagerUpdateConfig,
@@ -15,6 +14,7 @@ import type {
15
14
  TeleBoxManagerConfig,
16
15
  TeleBoxColorScheme,
17
16
  TeleBoxRect,
17
+ TeleBoxConfig,
18
18
  } from "@netless/telebox-insider";
19
19
  import type Emittery from "emittery";
20
20
  import type { NetlessApp } from "./typings";
@@ -57,6 +57,7 @@ export type BoxManagerContext = {
57
57
  canOperate: () => boolean;
58
58
  notifyContainerRectUpdate: (rect: TeleBoxRect) => void;
59
59
  cleanFocus: () => void;
60
+ setAppFocus: (appId: string) => void;
60
61
  };
61
62
 
62
63
  export const createBoxManager = (
@@ -74,6 +75,7 @@ export const createBoxManager = (
74
75
  notifyContainerRectUpdate: (rect: TeleBoxRect) =>
75
76
  manager.appManager?.notifyContainerRectUpdate(rect),
76
77
  cleanFocus: () => manager.appManager?.store.cleanFocus(),
78
+ setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
77
79
  callbacks,
78
80
  emitter,
79
81
  },
@@ -90,17 +92,32 @@ export class BoxManager {
90
92
  ) {
91
93
  const { emitter, callbacks } = context;
92
94
  this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
93
- this.teleBoxManager.events.on(TELE_BOX_MANAGER_EVENT.State, state => {
94
- if (state) {
95
- this.context.callbacks.emit("boxStateChange", state);
96
- this.context.emitter.emit("boxStateChange", state);
97
- }
95
+
96
+ // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
97
+ this.teleBoxManager._state$.reaction(state => {
98
+ callbacks.emit("boxStateChange", state);
99
+ emitter.emit("boxStateChange", state);
100
+ });
101
+
102
+ this.teleBoxManager._darkMode$.reaction(darkMode => {
103
+ callbacks.emit("darkModeChange", darkMode);
98
104
  });
105
+ this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
106
+ callbacks.emit("prefersColorSchemeChange", colorScheme);
107
+ });
108
+
109
+ // events.on 的值则会根据 skipUpdate 来决定是否触发回调
99
110
  this.teleBoxManager.events.on("minimized", minimized => {
100
111
  this.context.safeSetAttributes({ minimized });
101
112
  if (minimized) {
102
113
  this.context.cleanFocus();
103
114
  this.blurAllBox();
115
+ } else {
116
+ const topBox = this.getTopBox();
117
+ if (topBox) {
118
+ this.context.setAppFocus(topBox.id);
119
+ this.focusBox({ appId: topBox.id }, false);
120
+ }
104
121
  }
105
122
  });
106
123
  this.teleBoxManager.events.on("maximized", maximized => {
@@ -136,12 +153,6 @@ export class BoxManager {
136
153
  }
137
154
  }
138
155
  });
139
- this.teleBoxManager.events.on("dark_mode", darkMode => {
140
- callbacks.emit("darkModeChange", darkMode);
141
- });
142
- this.teleBoxManager.events.on("prefers_color_scheme", colorScheme => {
143
- callbacks.emit("prefersColorSchemeChange", colorScheme);
144
- });
145
156
  this.teleBoxManager.events.on("z_index", box => {
146
157
  this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
147
158
  });
@@ -150,7 +161,7 @@ export class BoxManager {
150
161
 
151
162
  private playgroundSizeChangeListener = () => {
152
163
  this.updateManagerRect();
153
- }
164
+ };
154
165
 
155
166
  private get mainView() {
156
167
  return this.context.getMainView();
@@ -281,8 +292,7 @@ export class BoxManager {
281
292
  }
282
293
 
283
294
  public getTopBox(): ReadonlyTeleBox | undefined {
284
- const boxes = this.teleBoxManager.query();
285
- return maxBy(boxes, "zIndex");
295
+ return this.teleBoxManager.topBox;
286
296
  }
287
297
 
288
298
  public updateBoxState(state?: AppInitState): void {
@@ -359,9 +369,9 @@ export class BoxManager {
359
369
  this.teleBoxManager.updateAll(config);
360
370
  }
361
371
 
362
- public setMaximized(maximized: boolean) {
372
+ public setMaximized(maximized: boolean, skipUpdate = true): void {
363
373
  if (maximized !== this.maximized) {
364
- this.teleBoxManager.setMaximized(maximized, true);
374
+ this.teleBoxManager.setMaximized(maximized, skipUpdate);
365
375
  }
366
376
  }
367
377
 
@@ -379,6 +389,10 @@ export class BoxManager {
379
389
  }
380
390
  }
381
391
 
392
+ public updateBox(id: string, payload: TeleBoxConfig, skipUpdate = true): void {
393
+ this.teleBoxManager.update(id, payload, skipUpdate);
394
+ }
395
+
382
396
  public setReadonly(readonly: boolean) {
383
397
  this.teleBoxManager.setReadonly(readonly);
384
398
  }
@@ -1,7 +1,6 @@
1
1
  import AppDocsViewer from "@netless/app-docs-viewer";
2
2
  import AppMediaPlayer, { setOptions } from "@netless/app-media-player";
3
3
  import { WindowManager } from "./index";
4
- import "@netless/app-docs-viewer/dist/style.css";
5
4
 
6
5
  export const setupBuiltin = () => {
7
6
  if (WindowManager.debug) {
@@ -1,6 +1,6 @@
1
1
  import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
2
2
  import { WindowManager } from "./index";
3
- import type { EmitterType} from "./index";
3
+ import type { EmitterType } from "./index";
4
4
 
5
5
  const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
6
6
 
@@ -13,7 +13,7 @@ export class ContainerResizeObserver {
13
13
  container: HTMLElement,
14
14
  sizer: HTMLElement,
15
15
  wrapper: HTMLDivElement,
16
- emitter: EmitterType,
16
+ emitter: EmitterType
17
17
  ) {
18
18
  const containerResizeObserver = new ContainerResizeObserver(emitter);
19
19
  containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
@@ -31,7 +31,7 @@ export class ContainerResizeObserver {
31
31
  const containerRect = entries[0]?.contentRect;
32
32
  if (containerRect) {
33
33
  this.updateSizer(containerRect, sizer, wrapper);
34
- this.emitter.emit("playgroundSizeChange", containerRect)
34
+ this.emitter.emit("playgroundSizeChange", containerRect);
35
35
  }
36
36
  });
37
37
 
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { isEmpty } from "lodash";
3
+ import { ApplianceNames } from "white-web-sdk";
3
4
 
4
5
  export let cursorName: string;
5
6
  export let tagName: string;
@@ -19,6 +20,7 @@
19
20
  $: hasTagName = !isEmpty(tagName);
20
21
  $: hasAvatar = !isEmpty(avatar);
21
22
  $: display = visible ? "initial" : "none";
23
+ $: isLaserPointer = appliance === ApplianceNames.laserPointer;
22
24
 
23
25
  const computedAvatarStyle = () => {
24
26
  return Object.entries({
@@ -36,28 +38,30 @@
36
38
  <div
37
39
  class="netless-window-manager-cursor-mid"
38
40
  style="transform: translateX({x}px) translateY({y}px);display: {display}"
39
- >
40
- <div class="netless-window-manager-cursor-name">
41
- <div
42
- class={theme}
43
- style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
44
- >
45
- {#if hasAvatar}
46
- <img
47
- class="netless-window-manager-cursor-selector-avatar"
48
- style={computedAvatarStyle()}
49
- src={avatar}
50
- alt="avatar"
51
- />
52
- {/if}
53
- <span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;max-width: 80px">{cursorName}</span>
54
- {#if hasTagName}
55
- <span class="netless-window-manager-cursor-tag-name" style="background-color: {cursorTagBackgroundColor}">
56
- {tagName}
57
- </span>
58
- {/if}
41
+ >
42
+ {#if !isLaserPointer}
43
+ <div class="netless-window-manager-cursor-name">
44
+ <div
45
+ class={theme}
46
+ style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
47
+ >
48
+ {#if hasAvatar}
49
+ <img
50
+ class="netless-window-manager-cursor-selector-avatar"
51
+ style={computedAvatarStyle()}
52
+ src={avatar}
53
+ alt="avatar"
54
+ />
55
+ {/if}
56
+ <span style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;max-width: 80px">{cursorName}</span>
57
+ {#if hasTagName}
58
+ <span class="netless-window-manager-cursor-tag-name" style="background-color: {cursorTagBackgroundColor}">
59
+ {tagName}
60
+ </span>
61
+ {/if}
62
+ </div>
59
63
  </div>
60
- </div>
64
+ {/if}
61
65
  <div class="cursor-image-wrapper">
62
66
  <img class="netless-window-manager-cursor-{appliance}-image" {src} alt={appliance} />
63
67
  </div>
@@ -1,42 +1,34 @@
1
- import App from './Cursor.svelte';
2
- import { ApplianceMap } from './icons';
3
- import { ApplianceNames } from 'white-web-sdk';
4
- import { CursorState } from '../constants';
5
- import { Fields } from '../AttributesDelegate';
6
- import { get, omit } from 'lodash';
7
- import type { Position } from '../AttributesDelegate';
1
+ import App from "./Cursor.svelte";
2
+ import { ApplianceMap } from "./icons";
3
+ import { ApplianceNames } from "white-web-sdk";
4
+ import { omit } from "lodash";
5
+ import type { Position } from "../AttributesDelegate";
8
6
  import type { RoomMember } from "white-web-sdk";
9
7
  import type { CursorManager } from "./index";
10
8
  import type { SvelteComponent } from "svelte";
11
- import { Base } from '../Base';
12
- import type { AppManager } from '../AppManager';
9
+ import type { AppManager } from "../AppManager";
13
10
 
14
11
  export type Payload = {
15
- [key: string]: any
16
- }
17
-
12
+ [key: string]: any;
13
+ };
18
14
 
19
- export class Cursor extends Base {
15
+ export class Cursor {
20
16
  private member?: RoomMember;
21
17
  private timer?: number;
22
18
  private component?: SvelteComponent;
23
19
 
24
20
  constructor(
25
- manager: AppManager,
26
- addCursorChangeListener: (uid: string, callback: (position: Position, state: CursorState) => void) => void,
27
- private cursors: any,
21
+ private manager: AppManager,
28
22
  private memberId: string,
29
23
  private cursorManager: CursorManager,
30
- private wrapper?: HTMLElement,
24
+ private wrapper?: HTMLElement
31
25
  ) {
32
- super(manager);
33
- this.setMember();
26
+ this.updateMember();
34
27
  this.createCursor();
35
- addCursorChangeListener(this.memberId, this.onCursorChange);
36
28
  this.autoHidden();
37
29
  }
38
30
 
39
- private onCursorChange = (position: Position, state: CursorState) => {
31
+ public move = (position: Position) => {
40
32
  if (position.type === "main") {
41
33
  const rect = this.cursorManager.wrapperRect;
42
34
  if (this.component && rect) {
@@ -45,7 +37,6 @@ export class Cursor extends Base {
45
37
  }
46
38
  } else {
47
39
  const focusView = this.cursorManager.focusView;
48
- // TODO 可以存一个当前 focusView 的 Rect 这样只有 focus 切换的时候才调用 getBoundingClientRect
49
40
  const viewRect = focusView?.divElement?.getBoundingClientRect();
50
41
  const viewCamera = focusView?.camera;
51
42
  if (focusView && viewRect && viewCamera && this.component) {
@@ -53,10 +44,11 @@ export class Cursor extends Base {
53
44
  this.moveCursor(position, viewRect, focusView);
54
45
  }
55
46
  }
56
- if (state && state === CursorState.Leave) {
57
- this.hide();
58
- }
59
- }
47
+ };
48
+
49
+ public leave = () => {
50
+ this.hide();
51
+ };
60
52
 
61
53
  private moveCursor(cursor: Position, rect: DOMRect, view: any) {
62
54
  const { x, y, type } = cursor;
@@ -124,21 +116,12 @@ export class Cursor extends Base {
124
116
  }
125
117
  }
126
118
 
127
- public get cursorState(): CursorState | undefined {
128
- return get(this.cursors, [this.memberId, Fields.CursorState]);
129
- }
130
-
131
- public get cursorPosition(): Position | undefined {
132
- return get(this.cursors, [this.memberId, Fields.Position]);
133
- }
134
-
135
119
  private autoHidden() {
136
120
  if (this.timer) {
137
121
  clearTimeout(this.timer);
138
122
  }
139
123
  this.timer = window.setTimeout(() => {
140
124
  this.hide();
141
- this.store.updateCursorState(this.memberId, CursorState.Leave);
142
125
  }, 1000 * 10); // 10 秒钟自动隐藏
143
126
  }
144
127
 
@@ -175,9 +158,10 @@ export class Cursor extends Base {
175
158
  }
176
159
  }
177
160
 
178
- public setMember() {
179
- this.member = this.context.findMemberByUid(this.memberId);
161
+ public updateMember() {
162
+ this.member = this.manager.findMemberByUid(this.memberId);
180
163
  this.updateComponent();
164
+ return this.member;
181
165
  }
182
166
 
183
167
  private updateComponent() {
@@ -188,13 +172,16 @@ export class Cursor extends Base {
188
172
  if (this.component) {
189
173
  this.component.$destroy();
190
174
  }
191
- this.manager.refresher?.remove(this.memberId);
192
175
  this.cursorManager.cursorInstances.delete(this.memberId);
176
+ if (this.timer) {
177
+ clearTimeout(this.timer);
178
+ }
193
179
  }
194
180
 
195
181
  public hide() {
196
182
  if (this.component) {
197
183
  this.component.$set({ visible: false });
184
+ this.destroy();
198
185
  }
199
186
  }
200
187
  }
@@ -4,6 +4,7 @@ import selector from "../image/selector-cursor.png";
4
4
  import eraser from "../image/eraser-cursor.png";
5
5
  import shape from "../image/shape-cursor.svg";
6
6
  import text from "../image/text-cursor.svg";
7
+ import laser from "../image/laser-pointer-cursor.svg";
7
8
 
8
9
  export const ApplianceMap: {
9
10
  [key: string]: string;
@@ -13,4 +14,5 @@ export const ApplianceMap: {
13
14
  [ApplianceNames.eraser]: eraser,
14
15
  [ApplianceNames.shape]: shape,
15
16
  [ApplianceNames.text]: text,
17
+ [ApplianceNames.laserPointer]: laser,
16
18
  };