@netless/window-manager 1.0.18 → 1.0.19
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/dist/index.d.ts +10 -0
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -3
- package/dist/index.mjs.map +1 -1
- package/docs/cn/box-size-change.md +144 -0
- package/package.json +3 -3
- package/src/App/AppBoxSizeSynchronizer.ts +124 -0
- package/src/App/AppContext.ts +2 -0
- package/src/App/AppProxy.ts +23 -0
- package/src/AppManager.ts +9 -0
- package/src/BoxManager.ts +15 -0
- package/src/typings.ts +2 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# `boxSizeChange` 接入指南
|
|
2
|
+
|
|
3
|
+
本文面向 Netless App(特别是 `@netless/app-slide`)维护者,说明如何使用
|
|
4
|
+
WindowManager 提供的 `boxSizeChange` 事件同步 App 内部渲染器尺寸。
|
|
5
|
+
|
|
6
|
+
## 适用场景
|
|
7
|
+
|
|
8
|
+
部分旧 Android WebView 虽然提供 `ResizeObserver` API,但在 CSS transition 或连续布局变化时,
|
|
9
|
+
回调可能只包含中间尺寸。此时 Box DOM 已经达到最终尺寸,App 内部渲染器仍可能保留错误的宽高。
|
|
10
|
+
|
|
11
|
+
WindowManager 会在 Box 布局稳定后读取 App 内容 DOM 的实际尺寸,并通过
|
|
12
|
+
`context.emitter` 发送 `boxSizeChange`。App 应使用该事件更新自己的 viewer、canvas 或 viewport,
|
|
13
|
+
不再以 App 内部的 `ResizeObserver` 作为最终尺寸的唯一来源。
|
|
14
|
+
|
|
15
|
+
## API
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
type BoxSizeChangePayload = {
|
|
19
|
+
appId: string;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
context.emitter.on(
|
|
25
|
+
"boxSizeChange",
|
|
26
|
+
(payload: BoxSizeChangePayload) => void | Promise<void>
|
|
27
|
+
): () => void;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
字段说明:
|
|
31
|
+
|
|
32
|
+
| 字段 | 类型 | 说明 |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| `appId` | `string` | 当前 App 实例 ID,与 `context.appId` 相同 |
|
|
35
|
+
| `width` | `number` | App 实际内容 DOM 的宽度,单位为 CSS pixel |
|
|
36
|
+
| `height` | `number` | App 实际内容 DOM 的高度,单位为 CSS pixel |
|
|
37
|
+
|
|
38
|
+
`width` 和 `height` 均为大于 0 的有限数值。它们不是 TeleBox 的相对模型尺寸,
|
|
39
|
+
也不是包含标题栏和边框的整个窗口尺寸。
|
|
40
|
+
|
|
41
|
+
## 触发时机
|
|
42
|
+
|
|
43
|
+
以下变化完成后可能触发事件:
|
|
44
|
+
|
|
45
|
+
- App setup 或 View mount;
|
|
46
|
+
- Box resize 或最小尺寸约束改变;
|
|
47
|
+
- 最大化、最小化、恢复和 Box 状态切换;
|
|
48
|
+
- WindowManager 容器尺寸变化;
|
|
49
|
+
- TeleBox 的实际视觉尺寸变化。
|
|
50
|
+
|
|
51
|
+
事件采用 trailing debounce,不是逐帧 resize 事件。当前实现会在最后一次触发后等待 650ms,
|
|
52
|
+
再通过两次间隔 100ms 的 DOM 采样确认尺寸。相同尺寸在 0.5px 容差内不会重复通知。
|
|
53
|
+
|
|
54
|
+
因此,App 不应依赖该事件实现拖拽过程中的逐帧动画,只应把它作为最终布局尺寸的同步信号。
|
|
55
|
+
|
|
56
|
+
## app-slide 接入示例
|
|
57
|
+
|
|
58
|
+
建议在 `setup(context)` 开始阶段立即注册监听。Slide viewer 可能异步创建,事件到达时如果 viewer
|
|
59
|
+
尚未就绪,应缓存最后一次尺寸,并在 viewer 创建完成后应用。
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
type BoxSize = { width: number; height: number };
|
|
63
|
+
|
|
64
|
+
export async function setup(context: AppContext): Promise<void> {
|
|
65
|
+
let viewer: SlideViewer | undefined;
|
|
66
|
+
let latestBoxSize: BoxSize | undefined;
|
|
67
|
+
|
|
68
|
+
const applyBoxSize = (size: BoxSize): void => {
|
|
69
|
+
latestBoxSize = size;
|
|
70
|
+
if (!viewer) return;
|
|
71
|
+
|
|
72
|
+
// 替换为 app-slide 当前 viewer 的实际尺寸更新 API。
|
|
73
|
+
viewer.resize(size.width, size.height);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const offBoxSizeChange = context.emitter.on(
|
|
77
|
+
"boxSizeChange",
|
|
78
|
+
({ appId, width, height }) => {
|
|
79
|
+
if (appId !== context.appId) return;
|
|
80
|
+
applyBoxSize({ width, height });
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const offDestroy = context.emitter.on("destroy", () => {
|
|
85
|
+
offBoxSizeChange();
|
|
86
|
+
offDestroy();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
viewer = await createSlideViewer(context);
|
|
90
|
+
|
|
91
|
+
if (latestBoxSize) {
|
|
92
|
+
applyBoxSize(latestBoxSize);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
如果 viewer 的尺寸 API 接收容器而不是数值,应在事件回调中调用对应的 layout/resize 方法,
|
|
98
|
+
但尺寸来源仍应以事件的 `width`、`height` 为准。
|
|
99
|
+
|
|
100
|
+
## 与 white-web-sdk View 的关系
|
|
101
|
+
|
|
102
|
+
WindowManager 在发送事件前会同步 App 对应的 white-web-sdk `View.size`:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
App 内容 DOM 最终尺寸
|
|
106
|
+
|
|
|
107
|
+
+--> white-web-sdk View.refreshSize(width, height)
|
|
108
|
+
|
|
|
109
|
+
+--> context.emitter: boxSizeChange
|
|
110
|
+
|
|
|
111
|
+
+--> app-slide viewer.resize(width, height)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
App 不需要调用 white-web-sdk 的 `View.refreshSize()`。该步骤由 WindowManager 负责;App 只需要
|
|
115
|
+
更新自身的 Slide viewer。两者解决的是不同层级的内部尺寸状态。
|
|
116
|
+
|
|
117
|
+
## 接入注意事项
|
|
118
|
+
|
|
119
|
+
1. 不要使用 `intrinsicWidth`、`intrinsicHeight` 替代事件尺寸。它们是相对 BoxManager 容器的模型值。
|
|
120
|
+
2. 不要在事件回调中再次修改 Box 宽高,否则可能形成“通知 -> 改 Box -> 再通知”的循环。
|
|
121
|
+
3. viewer 初始化期间只保留最新尺寸,不需要排队执行所有历史尺寸。
|
|
122
|
+
4. resize 操作应保持幂等;同一尺寸重复应用不应改变页面状态或课件同步状态。
|
|
123
|
+
5. 不要把本地像素尺寸写入协作 attributes。不同客户端的容器尺寸可能不同,该尺寸仅用于本地布局。
|
|
124
|
+
6. 监听器如果执行异步操作,应自行捕获业务异常。WindowManager 会记录监听器 rejection,
|
|
125
|
+
但无法替 App 恢复失败的 viewer 状态。
|
|
126
|
+
|
|
127
|
+
## 验收建议
|
|
128
|
+
|
|
129
|
+
至少覆盖以下场景:
|
|
130
|
+
|
|
131
|
+
- App 首次打开后,viewer 尺寸与内容 DOM 一致;
|
|
132
|
+
- 普通 Box resize 后,viewer 最终使用事件中的尺寸;
|
|
133
|
+
- 最大化、恢复、最小化后恢复均能得到正确尺寸;
|
|
134
|
+
- WindowManager 容器横竖屏切换或宿主尺寸变化后能重新同步;
|
|
135
|
+
- viewer 晚于首次事件创建时,缓存的最后尺寸会被应用;
|
|
136
|
+
- 连续收到相同尺寸时不会重复重建 viewer 或重置课件状态;
|
|
137
|
+
- Android 8.1 / WebView 71 中 PPT 最终铺满 App 内容区域;
|
|
138
|
+
- Room 与 Player、可写与只读模式下均不会因尺寸事件产生协作状态写入。
|
|
139
|
+
|
|
140
|
+
## 版本要求
|
|
141
|
+
|
|
142
|
+
接入方需要使用包含 `boxSizeChange` 的 WindowManager 版本或构建产物。仅升级 app-slide 而仍使用
|
|
143
|
+
旧 WindowManager 时不会收到该事件。white-web-sdk 的强制 View 尺寸同步由 WindowManager 兼容层处理,
|
|
144
|
+
不影响 app-slide 的事件监听代码。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/window-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "Multi-window mode for Netless Whiteboard",
|
|
5
5
|
"author": "l1shen <lishen1635@gmail.com> (https://github.com/l1shen)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"jspdf": "2.5.1",
|
|
26
|
-
"white-web-sdk": "^2.16.
|
|
26
|
+
"white-web-sdk": "^2.16.57"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"jspdf": {
|
|
@@ -73,6 +73,6 @@
|
|
|
73
73
|
"typescript": "^4.5.5",
|
|
74
74
|
"vite": "^2.9.9",
|
|
75
75
|
"vitest": "^0.14.1",
|
|
76
|
-
"white-web-sdk": "^2.16.
|
|
76
|
+
"white-web-sdk": "^2.16.57"
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { View } from "white-web-sdk";
|
|
2
|
+
|
|
3
|
+
export type AppBoxSize = { width: number; height: number };
|
|
4
|
+
export type AppBoxSizeChange = AppBoxSize & { appId: string };
|
|
5
|
+
|
|
6
|
+
type ViewWithRefreshSize = View & {
|
|
7
|
+
refreshSize?: (width: number, height: number) => void;
|
|
8
|
+
screen?: {
|
|
9
|
+
refreshSize?: (width: number, height: number) => void;
|
|
10
|
+
resizeObserver?: {
|
|
11
|
+
disconnect?: () => void;
|
|
12
|
+
observe?: (target: Element) => void;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const BOX_SIZE_EPSILON = 0.5;
|
|
18
|
+
const BOX_SIZE_SETTLE_DELAY = 650;
|
|
19
|
+
const BOX_SIZE_CONFIRM_DELAY = 100;
|
|
20
|
+
|
|
21
|
+
const sizesEqual = (left: AppBoxSize | undefined, right: AppBoxSize): boolean =>
|
|
22
|
+
Boolean(
|
|
23
|
+
left &&
|
|
24
|
+
Math.abs(left.width - right.width) <= BOX_SIZE_EPSILON &&
|
|
25
|
+
Math.abs(left.height - right.height) <= BOX_SIZE_EPSILON
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export class AppBoxSizeSynchronizer {
|
|
29
|
+
private frame?: number;
|
|
30
|
+
private timer?: ReturnType<typeof setTimeout>;
|
|
31
|
+
private pendingSize?: AppBoxSize;
|
|
32
|
+
private lastNotifiedSize?: AppBoxSize;
|
|
33
|
+
private destroyed = false;
|
|
34
|
+
|
|
35
|
+
public constructor(
|
|
36
|
+
private readonly appId: string,
|
|
37
|
+
private readonly getView: () => View | undefined,
|
|
38
|
+
private readonly getElement: () => Element | undefined,
|
|
39
|
+
private readonly notify: (payload: AppBoxSizeChange) => void,
|
|
40
|
+
private readonly onError?: (error: unknown) => void
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
public schedule = (): void => {
|
|
44
|
+
if (this.destroyed) return;
|
|
45
|
+
if (this.timer != null) clearTimeout(this.timer);
|
|
46
|
+
if (this.frame != null) {
|
|
47
|
+
cancelAnimationFrame(this.frame);
|
|
48
|
+
this.frame = undefined;
|
|
49
|
+
}
|
|
50
|
+
this.pendingSize = undefined;
|
|
51
|
+
this.timer = setTimeout(() => {
|
|
52
|
+
this.timer = undefined;
|
|
53
|
+
this.frame = requestAnimationFrame(this.confirmDOMSize);
|
|
54
|
+
}, BOX_SIZE_SETTLE_DELAY);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
public destroy(): void {
|
|
58
|
+
this.destroyed = true;
|
|
59
|
+
if (this.timer != null) {
|
|
60
|
+
clearTimeout(this.timer);
|
|
61
|
+
this.timer = undefined;
|
|
62
|
+
}
|
|
63
|
+
if (this.frame != null) {
|
|
64
|
+
cancelAnimationFrame(this.frame);
|
|
65
|
+
this.frame = undefined;
|
|
66
|
+
}
|
|
67
|
+
this.pendingSize = undefined;
|
|
68
|
+
this.lastNotifiedSize = undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private confirmDOMSize = (): void => {
|
|
72
|
+
this.frame = undefined;
|
|
73
|
+
if (this.destroyed) return;
|
|
74
|
+
|
|
75
|
+
const element = this.getElement();
|
|
76
|
+
if (!element) return;
|
|
77
|
+
|
|
78
|
+
const rect = element.getBoundingClientRect();
|
|
79
|
+
const size = { width: rect.width, height: rect.height };
|
|
80
|
+
if (
|
|
81
|
+
!Number.isFinite(size.width) ||
|
|
82
|
+
!Number.isFinite(size.height) ||
|
|
83
|
+
size.width <= 0 ||
|
|
84
|
+
size.height <= 0
|
|
85
|
+
) {
|
|
86
|
+
this.pendingSize = undefined;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!sizesEqual(this.pendingSize, size)) {
|
|
91
|
+
this.pendingSize = size;
|
|
92
|
+
this.timer = setTimeout(() => {
|
|
93
|
+
this.timer = undefined;
|
|
94
|
+
this.frame = requestAnimationFrame(this.confirmDOMSize);
|
|
95
|
+
}, BOX_SIZE_CONFIRM_DELAY);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.pendingSize = undefined;
|
|
100
|
+
const view = this.getView() as ViewWithRefreshSize | undefined;
|
|
101
|
+
if (view && !sizesEqual(view.size, size)) {
|
|
102
|
+
try {
|
|
103
|
+
if (typeof view.refreshSize === "function") {
|
|
104
|
+
view.refreshSize(size.width, size.height);
|
|
105
|
+
} else if (typeof view.screen?.refreshSize === "function") {
|
|
106
|
+
const resizeObserver = view.screen.resizeObserver;
|
|
107
|
+
resizeObserver?.disconnect?.();
|
|
108
|
+
try {
|
|
109
|
+
view.screen.refreshSize(size.width, size.height);
|
|
110
|
+
} finally {
|
|
111
|
+
if (view.divElement) resizeObserver?.observe?.(view.divElement);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
this.onError?.(error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!sizesEqual(this.lastNotifiedSize, size)) {
|
|
120
|
+
this.lastNotifiedSize = size;
|
|
121
|
+
this.notify({ appId: this.appId, ...size });
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
package/src/App/AppContext.ts
CHANGED
|
@@ -113,9 +113,11 @@ export class AppContext<TAttributes extends {} = any, TMagixEventPayloads = any,
|
|
|
113
113
|
const view = this.getView();
|
|
114
114
|
if (view) {
|
|
115
115
|
view.divElement = dom as HTMLDivElement;
|
|
116
|
+
this.appProxy.scheduleBoxSizeSync();
|
|
116
117
|
setTimeout(() => {
|
|
117
118
|
// 渲染需要时间,延迟 refresh
|
|
118
119
|
this.getRoom()?.refreshViewSize();
|
|
120
|
+
this.appProxy.scheduleBoxSizeSync();
|
|
119
121
|
callbacks.emit("onAppViewMounted", { appId: this.appId, view });
|
|
120
122
|
}, 1000);
|
|
121
123
|
}
|
package/src/App/AppProxy.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { calculateNextIndex } from "../Page";
|
|
|
34
34
|
import { boxEmitter } from "../BoxEmitter";
|
|
35
35
|
import { callbacks } from "../callback";
|
|
36
36
|
import { getExtendClass } from "../Utils/extendClass";
|
|
37
|
+
import { AppBoxSizeSynchronizer } from "./AppBoxSizeSynchronizer";
|
|
37
38
|
|
|
38
39
|
export type AppEmitter = Emittery<AppEmitterEvent>;
|
|
39
40
|
|
|
@@ -56,6 +57,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
56
57
|
private stateKey: string;
|
|
57
58
|
private _pageState: AppPageStateImpl;
|
|
58
59
|
private _prevFullPath: string | undefined;
|
|
60
|
+
private boxSizeSynchronizer: AppBoxSizeSynchronizer;
|
|
59
61
|
|
|
60
62
|
public appResult?: NetlessApp<any>;
|
|
61
63
|
public appContext?: AppContext<any, any>;
|
|
@@ -71,6 +73,21 @@ export class AppProxy implements PageRemoveService {
|
|
|
71
73
|
this.stateKey = `${this.id}_state`;
|
|
72
74
|
this.appProxies.set(this.id, this);
|
|
73
75
|
this.appEmitter = new Emittery();
|
|
76
|
+
this.boxSizeSynchronizer = new AppBoxSizeSynchronizer(
|
|
77
|
+
this.id,
|
|
78
|
+
() => this.view,
|
|
79
|
+
() => this.view?.divElement || this.box?.$content,
|
|
80
|
+
payload => {
|
|
81
|
+
this.appEmitter.emit("boxSizeChange", payload).catch(error => {
|
|
82
|
+
this.Logger?.error(
|
|
83
|
+
`[WindowManager]: failed to notify app box size change: ${error}`
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
error => {
|
|
88
|
+
this.Logger?.error(`[WindowManager]: failed to refresh app view size: ${error}`);
|
|
89
|
+
}
|
|
90
|
+
);
|
|
74
91
|
this.appListener = this.makeAppEventListener(this.id);
|
|
75
92
|
this.isAddApp = isAddApp;
|
|
76
93
|
|
|
@@ -104,6 +121,10 @@ export class AppProxy implements PageRemoveService {
|
|
|
104
121
|
return this.manager.viewManager.getView(this.id);
|
|
105
122
|
}
|
|
106
123
|
|
|
124
|
+
public scheduleBoxSizeSync = (): void => {
|
|
125
|
+
if (this.status !== "destroyed") this.boxSizeSynchronizer.schedule();
|
|
126
|
+
};
|
|
127
|
+
|
|
107
128
|
public get viewIndex(): number | undefined {
|
|
108
129
|
return this.view?.focusSceneIndex;
|
|
109
130
|
}
|
|
@@ -227,6 +248,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
227
248
|
);
|
|
228
249
|
const result = await app.setup(context);
|
|
229
250
|
this.appResult = result;
|
|
251
|
+
this.scheduleBoxSizeSync();
|
|
230
252
|
appRegister.notifyApp(this.kind, "created", { appId, result });
|
|
231
253
|
this.afterSetupApp(boxInitState);
|
|
232
254
|
this.fixMobileSize();
|
|
@@ -542,6 +564,7 @@ export class AppProxy implements PageRemoveService {
|
|
|
542
564
|
) {
|
|
543
565
|
if (this.status === "destroyed") return;
|
|
544
566
|
this.status = "destroyed";
|
|
567
|
+
this.boxSizeSynchronizer.destroy();
|
|
545
568
|
try {
|
|
546
569
|
await appRegister.notifyApp(this.kind, "destroy", { appId: this.id });
|
|
547
570
|
await this.appEmitter.emit("destroy", { error });
|
package/src/AppManager.ts
CHANGED
|
@@ -441,6 +441,15 @@ export class AppManager {
|
|
|
441
441
|
});
|
|
442
442
|
callbacks.emit("onBoxResize", payload);
|
|
443
443
|
}
|
|
444
|
+
this.scheduleAppBoxSizeSync(payload.appId);
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
public scheduleAppBoxSizeSync = (appId?: string): void => {
|
|
448
|
+
if (appId) {
|
|
449
|
+
this.appProxies.get(appId)?.scheduleBoxSizeSync();
|
|
450
|
+
} else {
|
|
451
|
+
this.appProxies.forEach(appProxy => appProxy.scheduleBoxSizeSync());
|
|
452
|
+
}
|
|
444
453
|
};
|
|
445
454
|
|
|
446
455
|
private onBoxFocus = (payload: BoxFocusPayload) => {
|
package/src/BoxManager.ts
CHANGED
|
@@ -66,6 +66,7 @@ export type BoxManagerContext = {
|
|
|
66
66
|
callbacks: CallbacksType;
|
|
67
67
|
canOperate: () => boolean;
|
|
68
68
|
notifyContainerRectUpdate: (rect: TeleBoxRect) => void;
|
|
69
|
+
scheduleAppBoxSizeSync: (appId?: string) => void;
|
|
69
70
|
cleanFocus: () => void;
|
|
70
71
|
setAppFocus: (appId: string) => void;
|
|
71
72
|
};
|
|
@@ -96,6 +97,8 @@ export const createBoxManager = (
|
|
|
96
97
|
canOperate: () => manager.canOperate,
|
|
97
98
|
notifyContainerRectUpdate: (rect: TeleBoxRect) =>
|
|
98
99
|
manager.appManager?.notifyContainerRectUpdate(rect),
|
|
100
|
+
scheduleAppBoxSizeSync: (appId?: string) =>
|
|
101
|
+
manager.appManager?.scheduleAppBoxSizeSync(appId),
|
|
99
102
|
cleanFocus: () => manager.appManager?.store.cleanFocus(),
|
|
100
103
|
setAppFocus: (appId: string) => manager.appManager?.store.setAppFocus(appId, true),
|
|
101
104
|
callbacks,
|
|
@@ -180,6 +183,9 @@ export class BoxManager {
|
|
|
180
183
|
});
|
|
181
184
|
}, 200)
|
|
182
185
|
);
|
|
186
|
+
this.teleBoxManager.events.on("visual_resize", (box: ReadonlyTeleBox): void => {
|
|
187
|
+
this.context.scheduleAppBoxSizeSync(box.id);
|
|
188
|
+
});
|
|
183
189
|
this.teleBoxManager.events.on("focused", box => {
|
|
184
190
|
if (box) {
|
|
185
191
|
if (this.canOperate) {
|
|
@@ -202,6 +208,7 @@ export class BoxManager {
|
|
|
202
208
|
this.teleBoxManager.events.on(
|
|
203
209
|
"box_status",
|
|
204
210
|
(box: { id: string; boxStatus?: TeleBoxState }) => {
|
|
211
|
+
this.context.scheduleAppBoxSizeSync(box.id);
|
|
205
212
|
if (this.canOperate) {
|
|
206
213
|
this.context.setBoxStatus(box.id, box.boxStatus);
|
|
207
214
|
}
|
|
@@ -299,6 +306,7 @@ export class BoxManager {
|
|
|
299
306
|
public setBoxesStatus(status?: Record<string, TeleBoxState>): void {
|
|
300
307
|
const map = new Map(Object.entries(status ?? {}));
|
|
301
308
|
this.teleBoxManager.setBoxesStatus(map, true);
|
|
309
|
+
this.context.scheduleAppBoxSizeSync();
|
|
302
310
|
this.context.callbacks.emit("onBoxesStatusChange", map);
|
|
303
311
|
this.context.emitter.emit("boxesStatusChange", map);
|
|
304
312
|
}
|
|
@@ -409,6 +417,7 @@ export class BoxManager {
|
|
|
409
417
|
if (state.minimized != null) {
|
|
410
418
|
this.teleBoxManager.setMinimized(Boolean(state.minimized), true);
|
|
411
419
|
}
|
|
420
|
+
this.context.scheduleAppBoxSizeSync(box.id);
|
|
412
421
|
}, 50);
|
|
413
422
|
if (!state.boxStatus) {
|
|
414
423
|
this.context.callbacks.emit("boxStateChange", this.teleBoxManager.state);
|
|
@@ -422,6 +431,7 @@ export class BoxManager {
|
|
|
422
431
|
const containerRect = { x: 0, y: 0, width: rect.width, height: rect.height };
|
|
423
432
|
this.teleBoxManager.setContainerRect(containerRect);
|
|
424
433
|
this.context.notifyContainerRectUpdate(this.teleBoxManager.containerRect);
|
|
434
|
+
this.context.scheduleAppBoxSizeSync();
|
|
425
435
|
}
|
|
426
436
|
}
|
|
427
437
|
|
|
@@ -435,6 +445,7 @@ export class BoxManager {
|
|
|
435
445
|
|
|
436
446
|
public resizeBox({ appId, width, height, skipUpdate }: ResizeBoxParams): void {
|
|
437
447
|
this.teleBoxManager.update(appId, { width, height }, skipUpdate);
|
|
448
|
+
this.context.scheduleAppBoxSizeSync(appId);
|
|
438
449
|
}
|
|
439
450
|
|
|
440
451
|
public setBoxMinSize(params: SetBoxMinSizeParams): void {
|
|
@@ -446,6 +457,7 @@ export class BoxManager {
|
|
|
446
457
|
},
|
|
447
458
|
true
|
|
448
459
|
);
|
|
460
|
+
this.context.scheduleAppBoxSizeSync(params.appId);
|
|
449
461
|
}
|
|
450
462
|
|
|
451
463
|
public setBoxTitle(params: SetBoxTitleParams): void {
|
|
@@ -463,11 +475,13 @@ export class BoxManager {
|
|
|
463
475
|
public setMaximized(maximized: boolean, skipUpdate = true): void {
|
|
464
476
|
if (maximized !== this.maximized) {
|
|
465
477
|
this.teleBoxManager.setMaximized(maximized, skipUpdate);
|
|
478
|
+
this.context.scheduleAppBoxSizeSync();
|
|
466
479
|
}
|
|
467
480
|
}
|
|
468
481
|
|
|
469
482
|
public setMinimized(minimized: boolean, skipUpdate = true) {
|
|
470
483
|
this.teleBoxManager.setMinimized(minimized, skipUpdate);
|
|
484
|
+
this.context.scheduleAppBoxSizeSync();
|
|
471
485
|
}
|
|
472
486
|
|
|
473
487
|
public focusTopBox(): void {
|
|
@@ -482,6 +496,7 @@ export class BoxManager {
|
|
|
482
496
|
|
|
483
497
|
public updateBox(id: string, payload: TeleBoxConfig, skipUpdate = true): void {
|
|
484
498
|
this.teleBoxManager.update(id, payload, skipUpdate);
|
|
499
|
+
this.context.scheduleAppBoxSizeSync(id);
|
|
485
500
|
}
|
|
486
501
|
|
|
487
502
|
public setReadonly(readonly: boolean) {
|
package/src/typings.ts
CHANGED
|
@@ -59,6 +59,8 @@ export type AppEmitterEvent<T = any> = {
|
|
|
59
59
|
seek: number;
|
|
60
60
|
pageStateChange: PageState;
|
|
61
61
|
boxStatusChange: { appId: string; status: TeleBoxState };
|
|
62
|
+
/** Actual app content size in CSS pixels after the box layout is applied. */
|
|
63
|
+
boxSizeChange: { appId: string; width: number; height: number };
|
|
62
64
|
};
|
|
63
65
|
|
|
64
66
|
export type RegisterEventData = {
|