@netless/window-manager 1.0.0-canary.9 → 1.0.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.
Files changed (132) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +90 -64
  3. package/README.zh-cn.md +224 -0
  4. package/dist/index.d.ts +1133 -40
  5. package/dist/index.js +62 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/{index.es.js → index.mjs} +9480 -6984
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/style.css +1 -1
  10. package/docs/advanced.md +55 -55
  11. package/docs/api.md +124 -113
  12. package/docs/app-context.md +248 -209
  13. package/docs/basic.md +25 -26
  14. package/docs/camera.md +19 -20
  15. package/docs/cn/advanced.md +137 -0
  16. package/docs/cn/api.md +309 -0
  17. package/docs/cn/app-context.md +369 -0
  18. package/docs/cn/basic.md +64 -0
  19. package/docs/cn/camera.md +53 -0
  20. package/docs/cn/concept.md +9 -0
  21. package/docs/cn/custom-max-bar.md +31 -0
  22. package/docs/cn/develop-app.md +94 -0
  23. package/docs/cn/export-pdf.md +48 -0
  24. package/docs/cn/migrate.md +60 -0
  25. package/docs/cn/replay.md +40 -0
  26. package/docs/concept.md +6 -5
  27. package/docs/custom-max-bar.md +31 -0
  28. package/docs/develop-app.md +22 -19
  29. package/docs/export-pdf.md +48 -0
  30. package/docs/migrate.md +25 -27
  31. package/docs/quickstart.md +50 -0
  32. package/docs/replay.md +20 -20
  33. package/package.json +32 -22
  34. package/src/App/AppContext.ts +105 -73
  35. package/src/App/AppPageStateImpl.ts +6 -25
  36. package/src/App/AppProxy.ts +41 -166
  37. package/src/App/MagixEvent/index.ts +38 -38
  38. package/src/App/Storage/StorageEvent.ts +13 -13
  39. package/src/App/Storage/index.ts +269 -245
  40. package/src/App/Storage/typings.ts +4 -2
  41. package/src/App/Storage/utils.ts +3 -3
  42. package/src/App/index.ts +0 -1
  43. package/src/AppListener.ts +8 -8
  44. package/src/AppManager.ts +88 -77
  45. package/src/AttributesDelegate.ts +42 -22
  46. package/src/BoxEmitter.ts +12 -6
  47. package/src/BoxManager.ts +128 -108
  48. package/src/ContainerResizeObserver.ts +75 -0
  49. package/src/Cursor/Cursor.svelte +16 -5
  50. package/src/Cursor/Cursor.svelte.d.ts +21 -0
  51. package/src/Cursor/Cursor.ts +77 -13
  52. package/src/Cursor/icons.ts +6 -0
  53. package/src/Cursor/icons2.ts +66 -0
  54. package/src/Cursor/index.ts +127 -26
  55. package/src/Helper.ts +94 -14
  56. package/src/InternalEmitter.ts +2 -7
  57. package/src/Page/index.ts +1 -1
  58. package/src/PageState.ts +6 -5
  59. package/src/ReconnectRefresher.ts +9 -4
  60. package/src/RedoUndo.ts +3 -3
  61. package/src/Register/index.ts +22 -17
  62. package/src/Register/loader.ts +26 -22
  63. package/src/Register/storage.ts +13 -13
  64. package/src/Utils/Common.ts +18 -14
  65. package/src/Utils/Reactive.ts +26 -25
  66. package/src/Utils/RoomHacker.ts +4 -4
  67. package/src/Utils/error.ts +0 -1
  68. package/src/View/IframeBridge.ts +680 -0
  69. package/src/View/MainView.ts +127 -53
  70. package/src/callback.ts +21 -1
  71. package/src/constants.ts +0 -2
  72. package/src/image/pencil-eraser-1.svg +3 -0
  73. package/src/image/pencil-eraser-2.svg +3 -0
  74. package/src/image/pencil-eraser-3.svg +3 -0
  75. package/src/index.ts +220 -83
  76. package/src/style.css +27 -10
  77. package/src/typings.ts +20 -10
  78. package/.prettierignore +0 -7
  79. package/.prettierrc.json +0 -9
  80. package/CHANGELOG.md +0 -196
  81. package/__mocks__/white-web-sdk.ts +0 -50
  82. package/dist/App/AppContext.d.ts +0 -76
  83. package/dist/App/AppPageStateImpl.d.ts +0 -21
  84. package/dist/App/AppProxy.d.ts +0 -86
  85. package/dist/App/AppViewSync.d.ts +0 -11
  86. package/dist/App/MagixEvent/index.d.ts +0 -29
  87. package/dist/App/Storage/StorageEvent.d.ts +0 -8
  88. package/dist/App/Storage/index.d.ts +0 -39
  89. package/dist/App/Storage/typings.d.ts +0 -22
  90. package/dist/App/Storage/utils.d.ts +0 -5
  91. package/dist/App/WhiteboardView.d.ts +0 -22
  92. package/dist/App/index.d.ts +0 -3
  93. package/dist/AppListener.d.ts +0 -21
  94. package/dist/AppManager.d.ts +0 -107
  95. package/dist/AttributesDelegate.d.ts +0 -80
  96. package/dist/BoxEmitter.d.ts +0 -34
  97. package/dist/BoxManager.d.ts +0 -99
  98. package/dist/BuiltinApps.d.ts +0 -5
  99. package/dist/Cursor/Cursor.d.ts +0 -39
  100. package/dist/Cursor/icons.d.ts +0 -3
  101. package/dist/Cursor/index.d.ts +0 -46
  102. package/dist/Helper.d.ts +0 -17
  103. package/dist/InternalEmitter.d.ts +0 -39
  104. package/dist/Page/PageController.d.ts +0 -21
  105. package/dist/Page/index.d.ts +0 -3
  106. package/dist/PageState.d.ts +0 -9
  107. package/dist/ReconnectRefresher.d.ts +0 -24
  108. package/dist/RedoUndo.d.ts +0 -18
  109. package/dist/Register/index.d.ts +0 -28
  110. package/dist/Register/loader.d.ts +0 -4
  111. package/dist/Register/storage.d.ts +0 -8
  112. package/dist/Utils/AppCreateQueue.d.ts +0 -15
  113. package/dist/Utils/Common.d.ts +0 -23
  114. package/dist/Utils/Reactive.d.ts +0 -6
  115. package/dist/Utils/RoomHacker.d.ts +0 -3
  116. package/dist/Utils/error.d.ts +0 -27
  117. package/dist/Utils/log.d.ts +0 -1
  118. package/dist/View/CameraSynchronizer.d.ts +0 -16
  119. package/dist/View/MainView.d.ts +0 -47
  120. package/dist/View/ViewManager.d.ts +0 -13
  121. package/dist/callback.d.ts +0 -24
  122. package/dist/constants.d.ts +0 -49
  123. package/dist/index.cjs.js +0 -46
  124. package/dist/index.umd.js +0 -46
  125. package/dist/typings.d.ts +0 -82
  126. package/jest.config.js +0 -27
  127. package/pnpm-lock.yaml +0 -6302
  128. package/src/App/AppViewSync.ts +0 -68
  129. package/src/App/WhiteboardView.ts +0 -83
  130. package/src/View/CameraSynchronizer.ts +0 -56
  131. package/vite.config.js +0 -51
  132. /package/docs/{qickstart.md → cn/quickstart.md} +0 -0
package/src/BoxManager.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AppAttributes, Events, MIN_HEIGHT, MIN_WIDTH } from "./constants";
2
2
  import { debounce } from "lodash";
3
- import { TELE_BOX_STATE, TeleBoxManager } from "@netless/telebox-insider";
3
+ import { TELE_BOX_STATE, TeleBoxCollector, TeleBoxManager } from "@netless/telebox-insider";
4
4
  import { WindowManager } from "./index";
5
5
  import type { BoxEmitterType } from "./BoxEmitter";
6
6
  import type { AddAppOptions, AppInitState } from "./index";
@@ -18,7 +18,6 @@ import type { NetlessApp } from "./typings";
18
18
  import type { View } from "white-web-sdk";
19
19
  import type { CallbacksType } from "./callback";
20
20
  import type { EmitterType } from "./InternalEmitter";
21
- import { SideEffectManager } from "side-effect-manager";
22
21
 
23
22
  export { TELE_BOX_STATE };
24
23
 
@@ -46,12 +45,11 @@ export type CreateTeleBoxManagerConfig = {
46
45
  collectorContainer?: HTMLElement;
47
46
  collectorStyles?: Partial<CSSStyleDeclaration>;
48
47
  prefersColorScheme?: TeleBoxColorScheme;
49
- stageRatio?: number;
50
- highlightStage?: boolean;
51
48
  };
52
49
 
53
50
  export type BoxManagerContext = {
54
51
  safeSetAttributes: (attributes: any) => void;
52
+ getMainView: () => View;
55
53
  updateAppState: (appId: string, field: AppAttributes, value: any) => void;
56
54
  emitter: EmitterType;
57
55
  boxEmitter: BoxEmitterType;
@@ -72,6 +70,7 @@ export const createBoxManager = (
72
70
  return new BoxManager(
73
71
  {
74
72
  safeSetAttributes: (attributes: any) => manager.safeSetAttributes(attributes),
73
+ getMainView: () => manager.mainView,
75
74
  updateAppState: (...args) => manager.appManager?.store.updateAppState(...args),
76
75
  canOperate: () => manager.canOperate,
77
76
  notifyContainerRectUpdate: (rect: TeleBoxRect) =>
@@ -80,7 +79,7 @@ export const createBoxManager = (
80
79
  setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
81
80
  callbacks,
82
81
  emitter,
83
- boxEmitter
82
+ boxEmitter,
84
83
  },
85
84
  options
86
85
  );
@@ -88,93 +87,95 @@ export const createBoxManager = (
88
87
 
89
88
  export class BoxManager {
90
89
  public teleBoxManager: TeleBoxManager;
91
- protected sideEffectManager: SideEffectManager;
92
90
 
93
91
  constructor(
94
92
  private context: BoxManagerContext,
95
- createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
93
+ private createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
96
94
  ) {
97
- this.sideEffectManager = new SideEffectManager();
98
95
  const { emitter, callbacks, boxEmitter } = context;
99
96
  this.teleBoxManager = this.setupBoxManager(createTeleBoxManagerConfig);
100
- this.sideEffectManager.add(() => [
101
- // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
102
- this.teleBoxManager._state$.reaction(state => {
103
- callbacks.emit("boxStateChange", state);
104
- emitter.emit("boxStateChange", state);
105
- }),
106
- this.teleBoxManager._darkMode$.reaction(darkMode => {
107
- callbacks.emit("darkModeChange", darkMode);
108
- }),
109
- this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
110
- callbacks.emit("prefersColorSchemeChange", colorScheme);
111
- }),
112
- this.teleBoxManager._minimized$.reaction((minimized, skipUpdate) => {
113
- if (skipUpdate) {
114
- return;
115
- }
116
- this.context.safeSetAttributes({ minimized });
117
- if (minimized) {
118
- this.context.cleanFocus();
119
- this.blurAllBox();
120
- } else {
121
- const topBox = this.getTopBox();
122
- if (topBox) {
123
- this.context.setAppFocus(topBox.id);
124
- this.focusBox({ appId: topBox.id }, false);
125
- }
126
- }
127
- }),
128
- this.teleBoxManager._maximized$.reaction((maximized, skipUpdate) => {
129
- if (skipUpdate) {
130
- return;
97
+
98
+ // 使用 _xxx$.reaction 订阅修改的值, 不管有没有 skipUpdate, 修改值都会触发回调
99
+ this.teleBoxManager._state$.reaction(state => {
100
+ callbacks.emit("boxStateChange", state);
101
+ emitter.emit("boxStateChange", state);
102
+ });
103
+
104
+ this.teleBoxManager._darkMode$.reaction(darkMode => {
105
+ callbacks.emit("darkModeChange", darkMode);
106
+ });
107
+ this.teleBoxManager._prefersColorScheme$.reaction(colorScheme => {
108
+ callbacks.emit("prefersColorSchemeChange", colorScheme);
109
+ });
110
+
111
+ // ppt 在最小化后刷新恢复正常大小,拿不到正确的宽高,需要手动触发一下窗口的 resize
112
+ this.teleBoxManager._minimized$.reaction(minimized => {
113
+ if (!minimized) {
114
+ setTimeout(() => {
115
+ const offset = 0.001 * (Math.random() > 0.5 ? 1 : -1);
116
+ this.teleBoxManager.boxes.forEach(box => {
117
+ box.resize(box.intrinsicWidth + offset, box.intrinsicHeight + offset, true);
118
+ });
119
+ }, 400);
120
+ }
121
+ });
122
+
123
+ // events.on 的值则会根据 skipUpdate 来决定是否触发回调
124
+ this.teleBoxManager.events.on("minimized", minimized => {
125
+ this.context.safeSetAttributes({ minimized });
126
+ if (minimized) {
127
+ this.context.cleanFocus();
128
+ this.blurAllBox();
129
+ } else {
130
+ const topBox = this.getTopBox();
131
+ if (topBox) {
132
+ this.context.setAppFocus(topBox.id);
133
+ this.focusBox({ appId: topBox.id }, false);
131
134
  }
132
- this.context.safeSetAttributes({ maximized });
133
- }),
134
- this.teleBoxManager.events.on("removed", boxes => {
135
- boxes.forEach(box => {
136
- boxEmitter.emit("close", { appId: box.id });
135
+ }
136
+ });
137
+ this.teleBoxManager.events.on("maximized", maximized => {
138
+ this.context.safeSetAttributes({ maximized });
139
+ });
140
+ this.teleBoxManager.events.on("removed", boxes => {
141
+ boxes.forEach(box => {
142
+ boxEmitter.emit("close", { appId: box.id });
143
+ });
144
+ });
145
+ this.teleBoxManager.events.on(
146
+ "intrinsic_move",
147
+ debounce((box: ReadonlyTeleBox): void => {
148
+ boxEmitter.emit("move", { appId: box.id, x: box.intrinsicX, y: box.intrinsicY });
149
+ }, 50)
150
+ );
151
+ this.teleBoxManager.events.on(
152
+ "intrinsic_resize",
153
+ debounce((box: ReadonlyTeleBox): void => {
154
+ boxEmitter.emit("resize", {
155
+ appId: box.id,
156
+ width: box.intrinsicWidth,
157
+ height: box.intrinsicHeight,
137
158
  });
138
- }),
139
- this.teleBoxManager.events.on(
140
- "intrinsic_move",
141
- debounce((box: ReadonlyTeleBox): void => {
142
- boxEmitter.emit("move", { appId: box.id, x: box.intrinsicX, y: box.intrinsicY });
143
- }, 50)
144
- ),
145
- this.teleBoxManager.events.on(
146
- "intrinsic_resize",
147
- debounce((box: ReadonlyTeleBox): void => {
148
- boxEmitter.emit("resize", {
149
- appId: box.id,
150
- width: box.intrinsicWidth,
151
- height: box.intrinsicHeight,
152
- });
153
- }, 200)
154
- ),
155
- this.teleBoxManager.events.on("focused", box => {
156
- if (box) {
157
- if (this.canOperate) {
158
- boxEmitter.emit("focus", { appId: box.id });
159
- } else {
160
- this.teleBoxManager.blurBox(box.id);
161
- }
159
+ }, 200)
160
+ );
161
+ this.teleBoxManager.events.on("focused", box => {
162
+ if (box) {
163
+ if (this.canOperate) {
164
+ boxEmitter.emit("focus", { appId: box.id });
165
+ } else {
166
+ this.teleBoxManager.blurBox(box.id);
162
167
  }
163
- }),
164
- this.teleBoxManager.events.on("z_index", box => {
165
- this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
166
- }),
167
- this.teleBoxManager._stageRect$.subscribe(stage => {
168
- emitter.emit("playgroundSizeChange", stage);
169
- this.context.notifyContainerRectUpdate(stage);
170
- }),
171
- emitter.on("writableChange", isWritable => {
172
- this.teleBoxManager.setHighlightStage(isWritable);
173
- }),
174
- emitter.on("containerSizeRatioUpdate", ratio => {
175
- this.teleBoxManager._stageRatio$.setValue(ratio);
176
- }),
177
- ]);
168
+ }
169
+ });
170
+ this.teleBoxManager.events.on("z_index", box => {
171
+ this.context.updateAppState(box.id, AppAttributes.ZIndex, box.zIndex);
172
+ });
173
+ emitter.on("playgroundSizeChange", () => this.updateManagerRect());
174
+ emitter.on("updateManagerRect", () => this.updateManagerRect());
175
+ }
176
+
177
+ private get mainView() {
178
+ return this.context.getMainView();
178
179
  }
179
180
 
180
181
  private get canOperate() {
@@ -205,16 +206,12 @@ export class BoxManager {
205
206
  return this.teleBoxManager.boxes.length;
206
207
  }
207
208
 
208
- public get stageRect() {
209
- return this.teleBoxManager.stageRect;
210
- }
211
-
212
- public createBox(params: CreateBoxParams): ReadonlyTeleBox | undefined {
209
+ public createBox(params: CreateBoxParams): void {
213
210
  if (!this.teleBoxManager) return;
214
211
  let { minwidth = MIN_WIDTH, minheight = MIN_HEIGHT } = params.app.config ?? {};
215
212
  const { width, height } = params.app.config ?? {};
216
213
  const title = params.options?.title || params.appId;
217
- const rect = this.teleBoxManager.rootRect;
214
+ const rect = this.teleBoxManager.containerRect;
218
215
 
219
216
  if (minwidth > 1) {
220
217
  minwidth = minwidth / rect.width;
@@ -232,21 +229,40 @@ export class BoxManager {
232
229
  height,
233
230
  id: params.appId,
234
231
  };
235
- const box = this.teleBoxManager.create(createBoxConfig, params.smartPosition);
232
+ this.teleBoxManager.create(createBoxConfig, params.smartPosition);
236
233
  this.context.emitter.emit(`${params.appId}${Events.WindowCreated}` as any);
237
- return box;
234
+ }
235
+
236
+ public setBoxInitState(appId: string): void {
237
+ const box = this.teleBoxManager.queryOne({ id: appId });
238
+ if (box) {
239
+ if (box.state === TELE_BOX_STATE.Maximized) {
240
+ this.context.boxEmitter.emit("resize", {
241
+ appId: appId,
242
+ x: box.x,
243
+ y: box.y,
244
+ width: box.intrinsicWidth,
245
+ height: box.intrinsicHeight,
246
+ });
247
+ }
248
+ }
238
249
  }
239
250
 
240
251
  public setupBoxManager(
241
252
  createTeleBoxManagerConfig?: CreateTeleBoxManagerConfig
242
253
  ): TeleBoxManager {
243
- const root = WindowManager.playground;
254
+ const root = WindowManager.wrapper ? WindowManager.wrapper : document.body;
255
+ const rect = root.getBoundingClientRect();
244
256
  const initManagerState: TeleBoxManagerConfig = {
245
- stageRatio: createTeleBoxManagerConfig?.stageRatio,
246
257
  root: root,
258
+ containerRect: {
259
+ x: 0,
260
+ y: 0,
261
+ width: rect.width,
262
+ height: rect.height,
263
+ },
247
264
  fence: false,
248
265
  prefersColorScheme: createTeleBoxManagerConfig?.prefersColorScheme,
249
- highlightStage: createTeleBoxManagerConfig?.highlightStage,
250
266
  };
251
267
 
252
268
  const manager = new TeleBoxManager(initManagerState);
@@ -254,16 +270,20 @@ export class BoxManager {
254
270
  this.teleBoxManager.destroy();
255
271
  }
256
272
  this.teleBoxManager = manager;
257
- const container = createTeleBoxManagerConfig?.collectorContainer;
273
+ const container = createTeleBoxManagerConfig?.collectorContainer || WindowManager.wrapper;
258
274
  if (container) {
259
- this.teleBoxManager.collector.set$collector(container);
260
- }
261
- if (createTeleBoxManagerConfig?.collectorStyles) {
262
- this.teleBoxManager.collector.setStyles(createTeleBoxManagerConfig.collectorStyles);
275
+ this.setCollectorContainer(container);
263
276
  }
264
277
  return manager;
265
278
  }
266
279
 
280
+ public setCollectorContainer(container: HTMLElement) {
281
+ const collector = new TeleBoxCollector({
282
+ styles: this.createTeleBoxManagerConfig?.collectorStyles,
283
+ }).mount(container);
284
+ this.teleBoxManager.setCollector(collector);
285
+ }
286
+
267
287
  public getBox(appId: string): ReadonlyTeleBox | undefined {
268
288
  return this.teleBoxManager.queryOne({ id: appId });
269
289
  }
@@ -316,6 +336,15 @@ export class BoxManager {
316
336
  }
317
337
  }
318
338
 
339
+ public updateManagerRect(): void {
340
+ const rect = this.mainView.divElement?.getBoundingClientRect();
341
+ if (rect && rect.width > 0 && rect.height > 0) {
342
+ const containerRect = { x: 0, y: 0, width: rect.width, height: rect.height };
343
+ this.teleBoxManager.setContainerRect(containerRect);
344
+ this.context.notifyContainerRectUpdate(this.teleBoxManager.containerRect);
345
+ }
346
+ }
347
+
319
348
  public moveBox({ appId, x, y }: MoveBoxParams): void {
320
349
  this.teleBoxManager.update(appId, { x, y }, true);
321
350
  }
@@ -387,16 +416,7 @@ export class BoxManager {
387
416
  this.teleBoxManager.update(id, { zIndex }, skipUpdate);
388
417
  }
389
418
 
390
- public setRoot(root: HTMLElement) {
391
- this.teleBoxManager._root$.setValue(root);
392
- }
393
-
394
- public setCollector(collector: HTMLElement) {
395
- this.teleBoxManager.collector.set$collector(collector);
396
- }
397
-
398
419
  public destroy() {
399
- this.sideEffectManager.flushAll();
400
420
  this.teleBoxManager.destroy();
401
421
  }
402
422
  }
@@ -0,0 +1,75 @@
1
+ import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
2
+ import { isFunction } from "lodash";
3
+ import { WindowManager } from "./index";
4
+ import type { EmitterType } from "./InternalEmitter";
5
+ import type { UnsubscribeFn } from "emittery";
6
+
7
+ const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
8
+
9
+ export class ContainerResizeObserver {
10
+ private containerResizeObserver?: ResizeObserver;
11
+ private disposer?: UnsubscribeFn;
12
+
13
+ constructor(private emitter: EmitterType) {}
14
+
15
+ public static create(
16
+ container: HTMLElement,
17
+ sizer: HTMLElement,
18
+ wrapper: HTMLDivElement,
19
+ emitter: EmitterType
20
+ ) {
21
+ const containerResizeObserver = new ContainerResizeObserver(emitter);
22
+ containerResizeObserver.observePlaygroundSize(container, sizer, wrapper);
23
+ return containerResizeObserver;
24
+ }
25
+
26
+ public observePlaygroundSize(
27
+ container: HTMLElement,
28
+ sizer: HTMLElement,
29
+ wrapper: HTMLDivElement
30
+ ) {
31
+ this.updateSizer(container.getBoundingClientRect(), sizer, wrapper);
32
+
33
+ this.containerResizeObserver = new ResizeObserver(entries => {
34
+ const containerRect = entries[0]?.contentRect;
35
+ if (containerRect) {
36
+ this.updateSizer(containerRect, sizer, wrapper);
37
+ this.emitter.emit("playgroundSizeChange", containerRect);
38
+ }
39
+ });
40
+
41
+ this.disposer = this.emitter.on("containerSizeRatioUpdate", () => {
42
+ const containerRect = container.getBoundingClientRect();
43
+ this.updateSizer(containerRect, sizer, wrapper);
44
+ this.emitter.emit("playgroundSizeChange", containerRect);
45
+ });
46
+
47
+ this.containerResizeObserver.observe(container);
48
+ }
49
+
50
+ public updateSizer(
51
+ { width, height }: DOMRectReadOnly,
52
+ sizer: HTMLElement,
53
+ wrapper: HTMLDivElement
54
+ ) {
55
+ if (width && height) {
56
+ if (height / width > WindowManager.containerSizeRatio) {
57
+ height = width * WindowManager.containerSizeRatio;
58
+ sizer.classList.toggle("netless-window-manager-sizer-horizontal", true);
59
+ } else {
60
+ width = height / WindowManager.containerSizeRatio;
61
+ sizer.classList.toggle("netless-window-manager-sizer-horizontal", false);
62
+ }
63
+ wrapper.style.width = `${width}px`;
64
+ wrapper.style.height = `${height}px`;
65
+ }
66
+ }
67
+
68
+ public disconnect() {
69
+ this.containerResizeObserver?.disconnect();
70
+ if (isFunction(this.disposer)) {
71
+ this.disposer();
72
+ this.disposer = undefined;
73
+ }
74
+ }
75
+ }
@@ -2,6 +2,7 @@
2
2
  import { isEmpty } from "lodash";
3
3
  import { ApplianceNames } from "white-web-sdk";
4
4
 
5
+ export let uid: string;
5
6
  export let cursorName: string;
6
7
  export let tagName: string | undefined;
7
8
  export let backgroundColor: string;
@@ -15,12 +16,17 @@
15
16
  export let color: string;
16
17
  export let cursorTagBackgroundColor: string;
17
18
  export let opacity: number;
19
+ export let pencilEraserSize: number | undefined;
20
+ export let custom: boolean | undefined;
18
21
 
19
22
  $: hasName = !isEmpty(cursorName);
20
23
  $: hasTagName = !isEmpty(tagName);
21
24
  $: hasAvatar = !isEmpty(avatar);
22
- $: display = visible ? "initial" : "none";
25
+ $: display = visible ? "" : "none";
23
26
  $: isLaserPointer = appliance === ApplianceNames.laserPointer;
27
+ $: isLaserPointerPencilEraser = isLaserPointer || appliance === ApplianceNames.pencilEraser;
28
+ $: offset = isLaserPointerPencilEraser ? "netless-window-manager-laserPointer-pencilEraser-offset" : "";
29
+ $: pencilEraserSize3ImageOffset = pencilEraserSize === 3 ? "netless-window-manager-pencilEraser-3-offset" : "";
24
30
 
25
31
  const computedAvatarStyle = () => {
26
32
  return Object.entries({
@@ -36,11 +42,12 @@
36
42
  </script>
37
43
 
38
44
  <div
39
- class="netless-window-manager-cursor-mid"
45
+ class={"netless-window-manager-cursor-mid" + (custom ? " netless-window-manager-cursor-custom" : "")}
40
46
  style="transform: translateX({x}px) translateY({y}px);display: {display}"
41
- >
47
+ data-cursor-uid={uid}
48
+ >
42
49
  {#if !isLaserPointer}
43
- <div class="netless-window-manager-cursor-name">
50
+ <div class="netless-window-manager-cursor-name {offset} {pencilEraserSize3ImageOffset}">
44
51
  <div
45
52
  class={theme}
46
53
  style="background-color: {backgroundColor};color: {color};opacity: {opacity}"
@@ -63,6 +70,10 @@
63
70
  </div>
64
71
  {/if}
65
72
  <div class="cursor-image-wrapper">
66
- <img class="netless-window-manager-cursor-{appliance}-image" {src} alt={appliance} />
73
+ <img
74
+ class="netless-window-manager-cursor-{appliance}-image {pencilEraserSize3ImageOffset}"
75
+ {src}
76
+ alt={appliance}
77
+ />
67
78
  </div>
68
79
  </div>
@@ -0,0 +1,21 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+
3
+ declare class Cursor extends SvelteComponentTyped<{
4
+ readonly cursorName: string;
5
+ readonly tagName?: string;
6
+ readonly backgroundColor: string;
7
+ readonly appliance: string;
8
+ readonly x: number;
9
+ readonly y: number;
10
+ readonly src?: string;
11
+ readonly visible: boolean;
12
+ readonly avatar: string;
13
+ readonly theme: string;
14
+ readonly color: string;
15
+ readonly cursorTagBackgroundColor: string;
16
+ readonly opacity: number;
17
+ readonly pencilEraserSize?: number;
18
+ readonly custom?: boolean;
19
+ }> {}
20
+
21
+ export default Cursor;
@@ -1,12 +1,15 @@
1
- import App from "./Cursor.svelte";
2
- import { ApplianceNames } from "white-web-sdk";
3
- import { findMemberByUid } from "../Helper";
4
- import { omit } from "lodash";
5
- import type { Position } from "../AttributesDelegate";
6
1
  import type { RoomMember } from "white-web-sdk";
7
- import type { CursorManager } from "./index";
8
- import type { SvelteComponent } from "svelte";
2
+ import type { CursorOptions } from "../index";
9
3
  import type { AppManager } from "../AppManager";
4
+ import type { Position } from "../AttributesDelegate";
5
+ import type { CursorManager } from "./index";
6
+
7
+ import { omit } from "lodash";
8
+ import { ApplianceNames } from "white-web-sdk";
9
+
10
+ import { findMemberByUid } from "../Helper";
11
+ import App from "./Cursor.svelte";
12
+ import { remoteIcon } from "./icons2";
10
13
 
11
14
  export type Payload = {
12
15
  [key: string]: any;
@@ -15,7 +18,8 @@ export type Payload = {
15
18
  export class Cursor {
16
19
  private member?: RoomMember;
17
20
  private timer?: number;
18
- private component?: SvelteComponent;
21
+ private component?: App;
22
+ private style: CursorOptions["style"] & string = "default";
19
23
 
20
24
  constructor(
21
25
  private manager: AppManager,
@@ -26,6 +30,7 @@ export class Cursor {
26
30
  this.updateMember();
27
31
  this.createCursor();
28
32
  this.autoHidden();
33
+ this.setStyle(cursorManager.style);
29
34
  }
30
35
 
31
36
  public move = (position: Position) => {
@@ -46,6 +51,16 @@ export class Cursor {
46
51
  }
47
52
  };
48
53
 
54
+ public setStyle = (style: typeof this.style) => {
55
+ this.style = style;
56
+ if (this.component) {
57
+ this.component.$set({
58
+ src: this.getIcon(),
59
+ custom: this.isCustomIcon(),
60
+ });
61
+ }
62
+ };
63
+
49
64
  public leave = () => {
50
65
  this.hide();
51
66
  };
@@ -56,6 +71,10 @@ export class Cursor {
56
71
  if (point) {
57
72
  let translateX = point.x - 2;
58
73
  let translateY = point.y - 18;
74
+ if (this.isCustomIcon()) {
75
+ translateX -= 11;
76
+ translateY += 4;
77
+ }
59
78
  if (type === "app") {
60
79
  const wrapperRect = this.cursorManager.wrapperRect;
61
80
  if (wrapperRect) {
@@ -80,6 +99,11 @@ export class Cursor {
80
99
  return `rgb(${rgb})`;
81
100
  }
82
101
 
102
+ public get memberColorHex(): string {
103
+ const [r, g, b] = this.member?.memberState?.strokeColor || [236, 52, 85];
104
+ return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
105
+ }
106
+
83
107
  private get payload(): Payload | undefined {
84
108
  return this.member?.payload;
85
109
  }
@@ -140,11 +164,13 @@ export class Cursor {
140
164
 
141
165
  private initProps() {
142
166
  return {
167
+ uid: this.memberId,
143
168
  x: 0,
144
169
  y: 0,
145
170
  appliance: this.memberApplianceName as string,
146
171
  avatar: this.memberAvatar,
147
172
  src: this.getIcon(),
173
+ custom: this.isCustomIcon(),
148
174
  visible: false,
149
175
  backgroundColor: this.memberColor,
150
176
  cursorName: this.memberCursorName,
@@ -153,15 +179,53 @@ export class Cursor {
153
179
  cursorTagBackgroundColor: this.memberCursorTagBackgroundColor,
154
180
  opacity: this.memberOpacity,
155
181
  tagName: this.memberTagName,
182
+ pencilEraserSize: this.member?.memberState.pencilEraserSize,
156
183
  };
157
184
  }
158
185
 
159
- private getIcon() {
160
- if (this.member) {
161
- const icons = this.cursorManager.applianceIcons;
162
- const applianceSrc = icons[this.memberApplianceName || ApplianceNames.shape];
163
- return applianceSrc || icons[ApplianceNames.shape];
186
+ private getIcon(): string | undefined {
187
+ if (!this.member) return;
188
+
189
+ const { memberApplianceName, memberColorHex } = this;
190
+ const { userApplianceIcons, applianceIcons } = this.cursorManager;
191
+
192
+ let iconsKey: string | undefined = this.memberApplianceName;
193
+ if (iconsKey === ApplianceNames.pencilEraser) {
194
+ iconsKey = `${iconsKey}${this.member?.memberState.pencilEraserSize || 1}`;
195
+ }
196
+
197
+ const userApplianceSrc = iconsKey && userApplianceIcons[iconsKey];
198
+ if (userApplianceSrc) return userApplianceSrc;
199
+
200
+ if (this.style === "custom" && memberApplianceName) {
201
+ const customApplianceSrc = remoteIcon(memberApplianceName, memberColorHex);
202
+ if (customApplianceSrc) return customApplianceSrc;
203
+ }
204
+
205
+ const applianceSrc = applianceIcons[iconsKey || ApplianceNames.shape];
206
+ return applianceSrc || applianceIcons[ApplianceNames.shape];
207
+ }
208
+
209
+ private isCustomIcon(): boolean {
210
+ if (!this.member) return false;
211
+
212
+ const { memberApplianceName, memberColorHex } = this;
213
+ const { userApplianceIcons } = this.cursorManager;
214
+
215
+ let iconsKey: string | undefined = this.memberApplianceName;
216
+ if (iconsKey === ApplianceNames.pencilEraser) {
217
+ iconsKey = `${iconsKey}${this.member?.memberState.pencilEraserSize || 1}`;
164
218
  }
219
+
220
+ const userApplianceSrc = iconsKey && userApplianceIcons[iconsKey];
221
+ if (userApplianceSrc) return false;
222
+
223
+ if (this.style === "custom" && memberApplianceName) {
224
+ const customApplianceSrc = remoteIcon(memberApplianceName, memberColorHex);
225
+ if (customApplianceSrc) return true;
226
+ }
227
+
228
+ return false;
165
229
  }
166
230
 
167
231
  public updateMember() {
@@ -5,6 +5,9 @@ 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
7
  import laser from "../image/laser-pointer-cursor.svg";
8
+ import pencilEraser1 from "../image/pencil-eraser-1.svg";
9
+ import pencilEraser2 from "../image/pencil-eraser-2.svg";
10
+ import pencilEraser3 from "../image/pencil-eraser-3.svg";
8
11
 
9
12
  export const ApplianceMap: {
10
13
  [key: string]: string;
@@ -15,4 +18,7 @@ export const ApplianceMap: {
15
18
  [ApplianceNames.shape]: shape,
16
19
  [ApplianceNames.text]: text,
17
20
  [ApplianceNames.laserPointer]: laser,
21
+ ["pencilEraser1"]: pencilEraser1,
22
+ ["pencilEraser2"]: pencilEraser2,
23
+ ["pencilEraser3"]: pencilEraser3,
18
24
  };