@netless/telebox-insider 0.2.25 → 1.0.0-alpha.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 (52) hide show
  1. package/dist/TeleBox/constants.d.ts +0 -3
  2. package/dist/TeleBox/index.d.ts +118 -97
  3. package/dist/TeleBox/typings.d.ts +42 -55
  4. package/dist/TeleBoxCollector/index.d.ts +25 -29
  5. package/dist/TeleBoxManager/MaxTitleBar/index.d.ts +14 -18
  6. package/dist/TeleBoxManager/constants.d.ts +0 -3
  7. package/dist/TeleBoxManager/index.d.ts +64 -19
  8. package/dist/TeleBoxManager/typings.d.ts +35 -28
  9. package/dist/TeleStage/index.d.ts +21 -0
  10. package/dist/TeleTitleBar/DefaultTitleBar/index.d.ts +6 -8
  11. package/dist/TeleTitleBar/typings.d.ts +3 -5
  12. package/dist/index.d.ts +0 -1
  13. package/dist/style.css +1 -1
  14. package/dist/telebox-insider.cjs.js +1 -15
  15. package/dist/telebox-insider.cjs.js.map +1 -1
  16. package/dist/telebox-insider.es.js +1 -15
  17. package/dist/telebox-insider.es.js.map +1 -1
  18. package/dist/utils.d.ts +0 -1
  19. package/package.json +7 -19
  20. package/src/TeleBox/constants.ts +0 -3
  21. package/src/TeleBox/index.ts +500 -690
  22. package/src/TeleBox/style.scss +6 -0
  23. package/src/TeleBox/typings.ts +42 -75
  24. package/src/TeleBoxCollector/index.ts +136 -159
  25. package/src/TeleBoxCollector/style.scss +3 -0
  26. package/src/TeleBoxManager/MaxTitleBar/index.ts +95 -108
  27. package/src/TeleBoxManager/MaxTitleBar/style.scss +3 -1
  28. package/src/TeleBoxManager/constants.ts +0 -3
  29. package/src/TeleBoxManager/index.ts +329 -336
  30. package/src/TeleBoxManager/typings.ts +42 -52
  31. package/src/TeleStage/index.ts +178 -0
  32. package/src/TeleStage/style.scss +49 -0
  33. package/src/TeleTitleBar/DefaultTitleBar/index.ts +48 -61
  34. package/src/TeleTitleBar/DefaultTitleBar/style.scss +7 -0
  35. package/src/TeleTitleBar/typings.ts +3 -9
  36. package/src/index.ts +0 -1
  37. package/src/scss/mixins.scss +7 -0
  38. package/src/utils.ts +0 -6
  39. package/dist/TeleBoxCollector/TeleBoxCollector.stories.d.ts +0 -5
  40. package/dist/TeleBoxManager/MaxTitleBar/MaxTitleBar.stories.d.ts +0 -7
  41. package/dist/TeleBoxManager/MaxTitleBar/MaxTitleBarReact.d.ts +0 -4
  42. package/dist/TeleBoxReact/TeleBox.stories.d.ts +0 -9
  43. package/dist/TeleBoxReact/index.d.ts +0 -17
  44. package/dist/TeleBoxReact/utils.d.ts +0 -2
  45. package/dist/TeleTitleBar/DefaultTitleBar/DefaultTitleBar.stories.d.ts +0 -4
  46. package/src/TeleBoxCollector/TeleBoxCollector.stories.tsx +0 -23
  47. package/src/TeleBoxManager/MaxTitleBar/MaxTitleBar.stories.tsx +0 -66
  48. package/src/TeleBoxManager/MaxTitleBar/MaxTitleBarReact.tsx +0 -34
  49. package/src/TeleBoxReact/TeleBox.stories.tsx +0 -74
  50. package/src/TeleBoxReact/index.tsx +0 -175
  51. package/src/TeleBoxReact/utils.ts +0 -14
  52. package/src/TeleTitleBar/DefaultTitleBar/DefaultTitleBar.stories.tsx +0 -15
@@ -1,141 +1,185 @@
1
- import EventEmitter from "eventemitter3";
2
1
  import shallowequal from "shallowequal";
2
+ import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
3
+ import { SideEffectManager } from "side-effect-manager";
3
4
  import type {
4
- TeleBoxConfig,
5
+ ReadonlyVal,
6
+ ValEnhancedResult,
7
+ ReadonlyValEnhancedResult,
8
+ } from "value-enhancer";
9
+ import {
10
+ combine,
11
+ Val,
12
+ withValueEnhancer,
13
+ withReadonlyValueEnhancer,
14
+ } from "value-enhancer";
15
+ import Emittery from "emittery";
16
+ import type {
17
+ ReadonlyTeleBox,
18
+ TeleBoxColorScheme,
5
19
  TeleBoxRect,
6
20
  TeleBoxState,
7
- } from "../TeleBox/typings";
21
+ } from "../TeleBox";
22
+ import {
23
+ TeleBox,
24
+ TELE_BOX_EVENT,
25
+ TELE_BOX_STATE,
26
+ TELE_BOX_COLOR_SCHEME,
27
+ TELE_BOX_DELEGATE_EVENT,
28
+ } from "../TeleBox";
8
29
  import { TeleBoxCollector } from "../TeleBoxCollector";
9
- import { ReadonlyTeleBox, TeleBox } from "../TeleBox";
10
- import { TELE_BOX_EVENT, TELE_BOX_STATE } from "../TeleBox/constants";
11
30
  import { TELE_BOX_MANAGER_EVENT } from "./constants";
12
31
  import type {
13
32
  TeleBoxManagerConfig,
14
33
  TeleBoxManagerCreateConfig,
15
- TeleBoxManagerEvents,
34
+ TeleBoxManagerEventConfig,
16
35
  TeleBoxManagerQueryConfig,
17
36
  TeleBoxManagerUpdateConfig,
18
37
  } from "./typings";
19
38
  import { MaxTitleBar } from "./MaxTitleBar";
20
- import {
21
- TeleBoxColorScheme,
22
- TELE_BOX_COLOR_SCHEME,
23
- TELE_BOX_DELEGATE_EVENT,
24
- } from "..";
25
- import { SideEffectManager } from "side-effect-manager";
26
- import {
27
- createSideEffectBinder,
28
- Val,
29
- ValEnhancedResult,
30
- withValueEnhancer,
31
- } from "value-enhancer";
39
+ import { TeleStage } from "../TeleStage";
32
40
 
33
41
  export * from "./typings";
34
42
  export * from "./constants";
35
43
 
44
+ const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
45
+
46
+ type ReadonlyValConfig = {
47
+ darkMode: ReadonlyVal<boolean, boolean>;
48
+ state: ReadonlyVal<TeleBoxState, boolean>;
49
+ root: Val<HTMLElement | null>;
50
+ rootRect: Val<TeleBoxRect>;
51
+ stageRect: ReadonlyVal<TeleBoxRect>;
52
+ };
53
+
36
54
  type ValConfig = {
37
55
  prefersColorScheme: Val<TeleBoxColorScheme, boolean>;
38
- containerRect: Val<TeleBoxRect, boolean>;
39
- collector: Val<TeleBoxCollector | null>;
40
- collectorRect: Val<TeleBoxRect | undefined>;
41
56
  readonly: Val<boolean, boolean>;
42
57
  minimized: Val<boolean, boolean>;
43
58
  maximized: Val<boolean, boolean>;
44
59
  fence: Val<boolean, boolean>;
60
+ stageRatio: Val<number, boolean>;
61
+ highlightStage: Val<boolean, boolean>;
45
62
  };
46
- export interface TeleBoxManager extends ValEnhancedResult<ValConfig> {}
63
+
64
+ type CombinedValEnhancedResult = ValEnhancedResult<ValConfig> &
65
+ ReadonlyValEnhancedResult<ReadonlyValConfig>;
66
+
67
+ export interface TeleBoxManager extends CombinedValEnhancedResult {}
47
68
 
48
69
  export class TeleBoxManager {
49
70
  public constructor({
50
- root = document.body,
71
+ root = null,
51
72
  prefersColorScheme = TELE_BOX_COLOR_SCHEME.Light,
52
73
  minimized = false,
53
74
  maximized = false,
54
- fence = true,
55
- containerRect = {
56
- x: 0,
57
- y: 0,
58
- width: window.innerWidth,
59
- height: window.innerHeight,
60
- },
75
+ fence = false,
61
76
  collector,
62
77
  namespace = "telebox",
63
78
  readonly = false,
79
+ stageRatio = -1,
80
+ highlightStage = true,
64
81
  }: TeleBoxManagerConfig = {}) {
65
82
  this._sideEffect = new SideEffectManager();
66
- const { combine, createVal } = createSideEffectBinder(this._sideEffect);
67
83
 
68
- this.root = root;
69
84
  this.namespace = namespace;
70
85
 
71
- this.boxes$ = createVal<TeleBox[]>([]);
72
- this.topBox$ = this.boxes$.derive((boxes) => {
73
- if (boxes.length > 0) {
74
- const topBox = boxes.reduce((topBox, box) =>
75
- topBox.zIndex > box.zIndex ? topBox : box
76
- );
77
- return topBox;
78
- }
79
- return;
80
- });
86
+ const root$ = new Val(root);
87
+ const readonly$ = new Val(readonly);
88
+ const minimized$ = new Val(minimized);
89
+ const maximized$ = new Val(maximized);
90
+ const fence$ = new Val(fence);
91
+ const stageRatio$ = new Val(stageRatio);
92
+ const highlightStage$ = new Val(highlightStage);
93
+
94
+ const rootRect$ = new Val<TeleBoxRect>(
95
+ {
96
+ x: 0,
97
+ y: 0,
98
+ width: window.innerWidth,
99
+ height: window.innerHeight,
100
+ },
101
+ { compare: shallowequal }
102
+ );
103
+ this._sideEffect.addDisposer(
104
+ root$.subscribe((root) => {
105
+ this._sideEffect.add(() => {
106
+ if (!root) {
107
+ return () => void 0;
108
+ }
109
+ const observer = new ResizeObserver((entries) => {
110
+ this._sideEffect.setTimeout(
111
+ // debounce
112
+ () => {
113
+ const rect = entries[0]?.contentRect;
114
+ if (rect) {
115
+ rootRect$.setValue({
116
+ x: rect.x,
117
+ y: rect.y,
118
+ width: rect.width,
119
+ height: rect.height,
120
+ });
121
+ }
122
+ },
123
+ 50,
124
+ "root-resize-observer"
125
+ );
126
+ });
127
+ observer.observe(root);
128
+ return () => observer.disconnect();
129
+ }, "calc-root-rect");
130
+ })
131
+ );
81
132
 
82
- const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
83
- const prefersDark$ = createVal(false);
133
+ this.boxes$ = new Val<TeleBox[]>([]);
134
+ this.topBox$ = new Val<TeleBox | undefined>(undefined);
135
+ this._sideEffect.addDisposer(
136
+ this.boxes$.subscribe((boxes) => {
137
+ if (boxes.length > 0) {
138
+ const topBox = boxes.reduce((topBox, box) =>
139
+ topBox.zIndex > box.zIndex ? topBox : box
140
+ );
141
+ this.topBox$.setValue(topBox);
142
+ } else {
143
+ this.topBox$.setValue(undefined);
144
+ }
145
+ })
146
+ );
84
147
 
85
- if (prefersDark) {
86
- prefersDark$.setValue(prefersDark.matches);
148
+ const prefersDarkMatch = window.matchMedia(
149
+ "(prefers-color-scheme: dark)"
150
+ );
151
+ const prefersDark$ = new Val(false);
152
+ if (prefersDarkMatch) {
153
+ prefersDark$.setValue(prefersDarkMatch.matches);
87
154
  this._sideEffect.add(() => {
88
155
  const handler = (evt: MediaQueryListEvent): void => {
89
156
  prefersDark$.setValue(evt.matches);
90
157
  };
91
- prefersDark.addListener(handler);
92
- return () => prefersDark.removeListener(handler);
158
+ prefersDarkMatch.addEventListener("change", handler);
159
+ return () =>
160
+ prefersDarkMatch.removeEventListener("change", handler);
93
161
  });
94
162
  }
95
163
 
96
- const prefersColorScheme$ = createVal(prefersColorScheme);
97
- prefersColorScheme$.reaction((prefersColorScheme, _, skipUpdate) => {
98
- this.boxes.forEach((box) =>
99
- box.setPrefersColorScheme(prefersColorScheme, skipUpdate)
100
- );
101
- if (!skipUpdate) {
102
- this.events.emit(
103
- TELE_BOX_MANAGER_EVENT.PrefersColorScheme,
104
- prefersColorScheme
105
- );
106
- }
107
- });
164
+ const prefersColorScheme$ = new Val(prefersColorScheme);
165
+ this._sideEffect.addDisposer(
166
+ prefersColorScheme$.reaction((prefersColorScheme, skipUpdate) => {
167
+ if (!skipUpdate) {
168
+ this.events.emit(
169
+ TELE_BOX_MANAGER_EVENT.PrefersColorScheme,
170
+ prefersColorScheme
171
+ );
172
+ }
173
+ })
174
+ );
108
175
 
109
- this._darkMode$ = combine(
176
+ const darkMode$ = combine(
110
177
  [prefersDark$, prefersColorScheme$],
111
178
  ([prefersDark, prefersColorScheme]) =>
112
179
  prefersColorScheme === "auto"
113
180
  ? prefersDark
114
181
  : prefersColorScheme === "dark"
115
182
  );
116
- this._darkMode$.reaction((darkMode, _, skipUpdate) => {
117
- this.boxes.forEach((box) => box.setDarkMode(darkMode, skipUpdate));
118
- if (!skipUpdate) {
119
- this.events.emit(TELE_BOX_MANAGER_EVENT.DarkMode, darkMode);
120
- }
121
- });
122
-
123
- const readonly$ = createVal(readonly);
124
- readonly$.reaction((readonly, _, skipUpdate) => {
125
- this.boxes.forEach((box) => box.setReadonly(readonly, skipUpdate));
126
- });
127
-
128
- const minimized$ = createVal(minimized);
129
-
130
- const maximized$ = createVal(maximized);
131
- maximized$.reaction((maximized, _, skipUpdate) => {
132
- this.boxes.forEach((box) =>
133
- box.setMaximized(maximized, skipUpdate)
134
- );
135
- if (!skipUpdate) {
136
- this.events.emit(TELE_BOX_MANAGER_EVENT.Maximized, maximized);
137
- }
138
- });
139
183
 
140
184
  const state$ = combine(
141
185
  [minimized$, maximized$],
@@ -146,101 +190,48 @@ export class TeleBoxManager {
146
190
  ? TELE_BOX_STATE.Maximized
147
191
  : TELE_BOX_STATE.Normal
148
192
  );
149
- state$.reaction((state, _, skipUpdate) => {
150
- this.maxTitleBar.setState(state);
151
- if (!skipUpdate) {
152
- this.events.emit(TELE_BOX_MANAGER_EVENT.State, state);
153
- }
154
- });
155
-
156
- const fence$ = createVal(fence);
157
- fence$.subscribe((fence, _, skipUpdate) => {
158
- this.boxes.forEach((box) => box.setFence(fence, skipUpdate));
159
- });
160
193
 
161
- const containerRect$ = createVal(containerRect, shallowequal);
162
- containerRect$.reaction((containerRect, _, skipUpdate) => {
163
- this.boxes.forEach((box) =>
164
- box.setContainerRect(containerRect, skipUpdate)
165
- );
166
- this.maxTitleBar.setContainerRect(containerRect);
167
- });
194
+ this.collector =
195
+ collector ??
196
+ new TeleBoxCollector({
197
+ minimized$: minimized$,
198
+ readonly$: readonly$,
199
+ darkMode$: darkMode$,
200
+ namespace,
201
+ root$,
202
+ rootRect$,
203
+ });
168
204
 
169
- const collector$ = createVal(
170
- collector === null
171
- ? null
172
- : collector ||
173
- new TeleBoxCollector({
174
- visible: minimized,
175
- readonly: readonly,
176
- namespace,
177
- }).mount(root)
178
- );
179
- collector$.subscribe((collector) => {
180
- if (collector) {
181
- collector.setVisible(minimized$.value);
182
- collector.setReadonly(readonly$.value);
183
- collector.setDarkMode(this._darkMode$.value);
184
- this._sideEffect.add(() => {
185
- collector.onClick = () => {
186
- if (!readonly$.value) {
187
- minimized$.setValue(false);
188
- }
189
- };
190
- return () => collector.destroy();
191
- }, "collect-onClick");
192
- }
193
- });
194
- readonly$.subscribe((readonly) =>
195
- collector$.value?.setReadonly(readonly)
196
- );
197
- this._darkMode$.subscribe((darkMode) => {
198
- collector$.value?.setDarkMode(darkMode);
205
+ const teleStage = new TeleStage({
206
+ namespace,
207
+ rootRect$,
208
+ ratio$: stageRatio$,
209
+ root$,
210
+ highlightStage$,
199
211
  });
200
-
201
- const calcCollectorRect = (): TeleBoxRect | undefined => {
202
- if (collector$.value?.$collector) {
203
- const { x, y, width, height } =
204
- collector$.value.$collector.getBoundingClientRect();
205
- const rootRect = this.root.getBoundingClientRect();
206
- return {
207
- x: x - rootRect.x,
208
- y: y - rootRect.y,
209
- width,
210
- height,
211
- };
212
- }
213
- return;
212
+ this._sideEffect.addDisposer(() => teleStage.destroy());
213
+
214
+ const readonlyValConfig: ReadonlyValConfig = {
215
+ darkMode: darkMode$,
216
+ state: state$,
217
+ root: root$,
218
+ rootRect: rootRect$,
219
+ stageRect: teleStage.stageRect$,
214
220
  };
215
221
 
216
- const collectorRect$ = createVal(
217
- minimized$.value ? calcCollectorRect() : void 0
218
- );
219
- collectorRect$.subscribe((collectorRect, _, skipUpdate) => {
220
- this.boxes.forEach((box) => {
221
- box.setCollectorRect(collectorRect, skipUpdate);
222
- });
223
- });
224
-
225
- minimized$.subscribe((minimized, _, skipUpdate) => {
226
- collector$.value?.setVisible(minimized);
227
-
228
- if (minimized) {
229
- if (collector$.value?.$collector) {
230
- collectorRect$.setValue(calcCollectorRect());
231
- } else if (import.meta.env.DEV) {
232
- console.warn("No collector for minimized boxes.");
233
- }
234
- }
222
+ withReadonlyValueEnhancer(this, readonlyValConfig);
235
223
 
236
- this.boxes.forEach((box) =>
237
- box.setMinimized(minimized, skipUpdate)
238
- );
224
+ const valConfig: ValConfig = {
225
+ prefersColorScheme: prefersColorScheme$,
226
+ readonly: readonly$,
227
+ fence: fence$,
228
+ minimized: minimized$,
229
+ maximized: maximized$,
230
+ stageRatio: stageRatio$,
231
+ highlightStage: highlightStage$,
232
+ };
239
233
 
240
- if (!skipUpdate) {
241
- this.events.emit(TELE_BOX_MANAGER_EVENT.Minimized, minimized);
242
- }
243
- });
234
+ withValueEnhancer(this, valConfig);
244
235
 
245
236
  const closeBtnClassName = this.wrapClassName("titlebar-icon-close");
246
237
 
@@ -287,13 +278,14 @@ export class TeleBoxManager {
287
278
  true
288
279
  );
289
280
 
290
- this.maxTitleBar = new MaxTitleBar({
291
- darkMode: this.darkMode,
292
- readonly: readonly$.value,
281
+ this.titleBar = new MaxTitleBar({
293
282
  namespace: this.namespace,
294
- state: state$.value,
295
- boxes: this.boxes$.value,
296
- containerRect: containerRect$.value,
283
+ boxes$: this.boxes$,
284
+ darkMode$: darkMode$,
285
+ readonly$: readonly$,
286
+ state$: state$,
287
+ root$,
288
+ rootRect$: rootRect$,
297
289
  onEvent: (event): void => {
298
290
  switch (event.type) {
299
291
  case TELE_BOX_DELEGATE_EVENT.Maximize: {
@@ -315,32 +307,35 @@ export class TeleBoxManager {
315
307
  }
316
308
  },
317
309
  });
318
- readonly$.subscribe((readonly) =>
319
- this.maxTitleBar.setReadonly(readonly)
320
- );
321
- this._darkMode$.subscribe((darkMode) => {
322
- this.maxTitleBar.setDarkMode(darkMode);
323
- });
324
- this.boxes$.reaction((boxes) => {
325
- this.maxTitleBar.setBoxes(boxes);
326
- });
327
-
328
- const valConfig: ValConfig = {
329
- prefersColorScheme: prefersColorScheme$,
330
- containerRect: containerRect$,
331
- collector: collector$,
332
- collectorRect: collectorRect$,
333
- readonly: readonly$,
334
- fence: fence$,
335
- minimized: minimized$,
336
- maximized: maximized$,
337
- };
338
-
339
- withValueEnhancer(this, valConfig);
340
310
 
341
- this._state$ = state$;
342
-
343
- this.root.appendChild(this.maxTitleBar.render());
311
+ this._sideEffect.addDisposer([
312
+ state$.reaction((state, skipUpdate) => {
313
+ if (!skipUpdate) {
314
+ this.events.emit(TELE_BOX_MANAGER_EVENT.State, state);
315
+ }
316
+ }),
317
+ maximized$.reaction((maximized, skipUpdate) => {
318
+ if (!skipUpdate) {
319
+ this.events.emit(
320
+ TELE_BOX_MANAGER_EVENT.Maximized,
321
+ maximized
322
+ );
323
+ }
324
+ }),
325
+ minimized$.reaction((minimized, skipUpdate) => {
326
+ if (!skipUpdate) {
327
+ this.events.emit(
328
+ TELE_BOX_MANAGER_EVENT.Minimized,
329
+ minimized
330
+ );
331
+ }
332
+ }),
333
+ darkMode$.reaction((darkMode, skipUpdate) => {
334
+ if (!skipUpdate) {
335
+ this.events.emit(TELE_BOX_MANAGER_EVENT.DarkMode, darkMode);
336
+ }
337
+ }),
338
+ ]);
344
339
  }
345
340
 
346
341
  public get boxes(): ReadonlyArray<TeleBox> {
@@ -351,26 +346,15 @@ export class TeleBoxManager {
351
346
  return this.topBox$.value;
352
347
  }
353
348
 
354
- public readonly events = new EventEmitter() as TeleBoxManagerEvents;
349
+ public readonly events = new Emittery<
350
+ TeleBoxManagerEventConfig,
351
+ TeleBoxManagerEventConfig
352
+ >();
355
353
 
356
354
  protected _sideEffect: SideEffectManager;
357
355
 
358
- protected root: HTMLElement;
359
-
360
356
  public readonly namespace: string;
361
357
 
362
- public _darkMode$: Val<boolean, boolean>;
363
-
364
- public get darkMode(): boolean {
365
- return this._darkMode$.value;
366
- }
367
-
368
- public _state$: Val<TeleBoxState, boolean>;
369
-
370
- public get state(): TeleBoxState {
371
- return this._state$.value;
372
- }
373
-
374
358
  /** @deprecated use setMaximized and setMinimized instead */
375
359
  public setState(state: TeleBoxState, skipUpdate = false): this {
376
360
  switch (state) {
@@ -399,20 +383,22 @@ export class TeleBoxManager {
399
383
  ): ReadonlyTeleBox {
400
384
  const box = new TeleBox({
401
385
  zIndex: this.topBox ? this.topBox.zIndex + 1 : 100,
402
- ...(smartPosition ? this.smartPosition(config) : config),
403
- darkMode: this.darkMode,
404
- prefersColorScheme: this.prefersColorScheme,
405
- maximized: this.maximized,
406
- minimized: this.minimized,
407
- fence: this.fence,
386
+ ...config,
387
+ ...(smartPosition ? this.smartPosition(config) : {}),
408
388
  namespace: this.namespace,
409
- containerRect: this.containerRect,
410
- readonly: this.readonly,
411
- collectorRect: this.collectorRect,
389
+ root$: this._root$,
390
+ darkMode$: this._darkMode$,
391
+ maximized$: this._maximized$,
392
+ minimized$: this._minimized$,
393
+ fence$: this._fence$,
394
+ rootRect$: this._rootRect$,
395
+ managerStageRect$: this._stageRect$,
396
+ stageRatio$: this._stageRatio$,
397
+ readonly$: this._readonly$,
398
+ collectorRect$: this.collector._rect$,
399
+ managerHighlightStage$: this._highlightStage$,
412
400
  });
413
401
 
414
- box.mount(this.root);
415
-
416
402
  if (box.focus) {
417
403
  this.focusBox(box);
418
404
  if (smartPosition) {
@@ -422,52 +408,43 @@ export class TeleBoxManager {
422
408
 
423
409
  this.boxes$.setValue([...this.boxes, box]);
424
410
 
425
- box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Maximize, () => {
426
- this.setMaximized(!this.maximized);
427
- });
428
- box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Minimize, () => {
429
- this.setMinimized(true);
430
- });
431
- box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Close, () => {
432
- this.remove(box);
433
- this.focusTopBox();
434
- });
435
- box._coord$.reaction((_, __, skipUpdate) => {
436
- if (!skipUpdate) {
437
- this.events.emit(TELE_BOX_MANAGER_EVENT.Move, box);
438
- }
439
- });
440
- box._size$.reaction((_, __, skipUpdate) => {
441
- if (!skipUpdate) {
442
- this.events.emit(TELE_BOX_MANAGER_EVENT.Resize, box);
443
- }
444
- });
445
- box._intrinsicCoord$.reaction((_, __, skipUpdate) => {
446
- if (!skipUpdate) {
447
- this.events.emit(TELE_BOX_MANAGER_EVENT.IntrinsicMove, box);
448
- }
449
- });
450
- box._intrinsicSize$.reaction((_, __, skipUpdate) => {
451
- if (!skipUpdate) {
452
- this.events.emit(TELE_BOX_MANAGER_EVENT.IntrinsicResize, box);
453
- }
454
- });
455
- box._visualSize$.reaction((_, __, skipUpdate) => {
456
- if (!skipUpdate) {
457
- this.events.emit(TELE_BOX_MANAGER_EVENT.VisualResize, box);
458
- }
459
- });
460
- box._zIndex$.reaction((_, __, skipUpdate) => {
461
- if (this.boxes.length > 0) {
462
- const topBox = this.boxes.reduce((topBox, box) =>
463
- topBox.zIndex > box.zIndex ? topBox : box
464
- );
465
- this.topBox$.setValue(topBox);
466
- }
467
- if (!skipUpdate) {
468
- this.events.emit(TELE_BOX_MANAGER_EVENT.ZIndex, box);
469
- }
470
- });
411
+ this._sideEffect.addDisposer([
412
+ box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Maximize, () => {
413
+ this.setMaximized(!this.maximized);
414
+ }),
415
+ box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Minimize, () => {
416
+ this.setMinimized(true);
417
+ }),
418
+ box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Close, () => {
419
+ this.remove(box);
420
+ this.focusTopBox();
421
+ }),
422
+ box._intrinsicCoord$.reaction((_, skipUpdate) => {
423
+ if (!skipUpdate) {
424
+ this.events.emit(TELE_BOX_MANAGER_EVENT.IntrinsicMove, box);
425
+ }
426
+ }),
427
+ box._intrinsicSize$.reaction((_, skipUpdate) => {
428
+ if (!skipUpdate) {
429
+ this.events.emit(
430
+ TELE_BOX_MANAGER_EVENT.IntrinsicResize,
431
+ box
432
+ );
433
+ }
434
+ }),
435
+ box._zIndex$.reaction((_, skipUpdate) => {
436
+ if (this.boxes.length > 0) {
437
+ const topBox = this.boxes.reduce((topBox, box) =>
438
+ topBox.zIndex > box.zIndex ? topBox : box
439
+ );
440
+ this.topBox$.setValue(topBox);
441
+ }
442
+ if (!skipUpdate) {
443
+ this.events.emit(TELE_BOX_MANAGER_EVENT.ZIndex, box);
444
+ }
445
+ }),
446
+ ]);
447
+
471
448
  this.events.emit(TELE_BOX_MANAGER_EVENT.Created, box);
472
449
 
473
450
  return box;
@@ -550,17 +527,26 @@ export class TeleBoxManager {
550
527
  return deletedBoxes;
551
528
  }
552
529
 
530
+ /**
531
+ * Mount manager to a container element.
532
+ */
533
+ public mount(root: HTMLElement): void {
534
+ this._root$.setValue(root);
535
+ }
536
+
537
+ /**
538
+ * Unmount manager from the container element.
539
+ */
540
+ public unmount(): void {
541
+ this._root$.setValue(null);
542
+ }
543
+
553
544
  public destroy(skipUpdate = false): void {
554
- this.events.removeAllListeners();
545
+ this.events.clearListeners();
555
546
  this._sideEffect.flushAll();
556
547
  this.removeAll(skipUpdate);
557
-
558
- Object.keys(this).forEach((key) => {
559
- const value = this[key as keyof this];
560
- if (value instanceof Val) {
561
- value.destroy();
562
- }
563
- });
548
+ this.collector.destroy();
549
+ this.titleBar.destroy();
564
550
  }
565
551
 
566
552
  public wrapClassName(className: string): string {
@@ -575,7 +561,7 @@ export class TeleBoxManager {
575
561
  let focusChanged = false;
576
562
  if (!targetBox.focus) {
577
563
  focusChanged = true;
578
- targetBox.setFocus(true, skipUpdate);
564
+ targetBox._focus$.setValue(true, skipUpdate);
579
565
  }
580
566
  if (focusChanged && !skipUpdate) {
581
567
  this.events.emit(
@@ -587,7 +573,7 @@ export class TeleBoxManager {
587
573
  this.blurBox(box, skipUpdate);
588
574
  }
589
575
  });
590
- this.maxTitleBar.focusBox(targetBox);
576
+ this.titleBar.focusBox(targetBox);
591
577
  }
592
578
  }
593
579
 
@@ -601,13 +587,13 @@ export class TeleBoxManager {
601
587
  const targetBox = this.getBox(boxOrID);
602
588
  if (targetBox) {
603
589
  if (targetBox.focus) {
604
- targetBox.setFocus(false, skipUpdate);
590
+ targetBox._focus$.setValue(false, skipUpdate);
605
591
  if (!skipUpdate) {
606
592
  this.events.emit(TELE_BOX_MANAGER_EVENT.Blurred, targetBox);
607
593
  }
608
594
  }
609
- if (this.maxTitleBar.focusedBox === targetBox) {
610
- this.maxTitleBar.focusBox();
595
+ if (this.titleBar.focusedBox === targetBox) {
596
+ this.titleBar.focusBox();
611
597
  }
612
598
  }
613
599
  }
@@ -615,18 +601,19 @@ export class TeleBoxManager {
615
601
  public blurAll(skipUpdate = false): void {
616
602
  this.boxes.forEach((box) => {
617
603
  if (box.focus) {
618
- box.setFocus(false, skipUpdate);
604
+ box._focus$.setValue(false, skipUpdate);
619
605
  if (!skipUpdate) {
620
606
  this.events.emit(TELE_BOX_MANAGER_EVENT.Blurred, box);
621
607
  }
622
608
  }
623
609
  });
624
- if (this.maxTitleBar.focusedBox) {
625
- this.maxTitleBar.focusBox();
610
+ if (this.titleBar.focusedBox) {
611
+ this.titleBar.focusBox();
626
612
  }
627
613
  }
628
614
 
629
- protected maxTitleBar: MaxTitleBar;
615
+ public collector: TeleBoxCollector;
616
+ public titleBar: MaxTitleBar;
630
617
 
631
618
  protected boxes$: Val<TeleBox[]>;
632
619
  protected topBox$: Val<TeleBox | undefined>;
@@ -647,43 +634,40 @@ export class TeleBoxManager {
647
634
  skipUpdate = false
648
635
  ): void {
649
636
  if (config.x != null || config.y != null) {
650
- box.move(
651
- config.x == null ? box.intrinsicX : config.x,
652
- config.y == null ? box.intrinsicY : config.y,
637
+ box._intrinsicCoord$.setValue(
638
+ {
639
+ x: config.x ?? box.intrinsicX,
640
+ y: config.y ?? box.intrinsicY,
641
+ },
653
642
  skipUpdate
654
643
  );
655
644
  }
656
645
  if (config.width != null || config.height != null) {
657
- box.resize(
658
- config.width == null ? box.intrinsicWidth : config.width,
659
- config.height == null ? box.intrinsicHeight : config.height,
646
+ box._intrinsicSize$.setValue(
647
+ {
648
+ width: config.width ?? box.intrinsicWidth,
649
+ height: config.height ?? box.intrinsicHeight,
650
+ },
660
651
  skipUpdate
661
652
  );
662
653
  }
663
654
  if (config.title != null) {
664
- box.setTitle(config.title);
665
- this.maxTitleBar.updateTitles();
655
+ box._title$.setValue(config.title);
666
656
  }
667
657
  if (config.visible != null) {
668
- box.setVisible(config.visible, skipUpdate);
669
- }
670
- if (config.minHeight != null) {
671
- box.setMinHeight(config.minHeight, skipUpdate);
672
- }
673
- if (config.minWidth != null) {
674
- box.setMinWidth(config.minWidth, skipUpdate);
658
+ box._visible$.setValue(config.visible, skipUpdate);
675
659
  }
676
660
  if (config.resizable != null) {
677
- box.setResizable(config.resizable, skipUpdate);
661
+ box._resizable$.setValue(config.resizable, skipUpdate);
678
662
  }
679
663
  if (config.draggable != null) {
680
- box.setDraggable(config.draggable, skipUpdate);
664
+ box._draggable$.setValue(config.draggable, skipUpdate);
681
665
  }
682
- if (config.fixRatio != null) {
683
- box.setFixRatio(config.fixRatio, skipUpdate);
666
+ if (config.ratio != null) {
667
+ box._ratio$.setValue(config.ratio, skipUpdate);
684
668
  }
685
669
  if (config.zIndex != null) {
686
- box.setZIndex(config.zIndex, skipUpdate);
670
+ box._zIndex$.setValue(config.zIndex, skipUpdate);
687
671
  }
688
672
  if (config.content != null) {
689
673
  box.mountContent(config.content);
@@ -691,52 +675,61 @@ export class TeleBoxManager {
691
675
  if (config.footer != null) {
692
676
  box.mountFooter(config.footer);
693
677
  }
678
+ if (config.minHeight != null || config.minWidth != null) {
679
+ box._minSize$.setValue(
680
+ {
681
+ width: config.minWidth ?? box.minWidth,
682
+ height: config.minHeight ?? box.minHeight,
683
+ },
684
+ skipUpdate
685
+ );
686
+ }
694
687
  }
695
688
 
696
- protected smartPosition(config: TeleBoxConfig = {}): TeleBoxConfig {
697
- let { x, y } = config;
698
- const { width = 0.5, height = 0.5 } = config;
689
+ /** Keep new boxes staggered inside stage area */
690
+ protected smartPosition(rect: Partial<TeleBoxRect>): TeleBoxRect {
691
+ let { x, y } = rect;
692
+ const { width = 0.5, height = 0.5 } = rect;
693
+ const stageRect = this.stageRect;
694
+ const topBox = this.topBox;
699
695
 
700
696
  if (x == null) {
701
- let vx = 20;
702
- if (this.topBox) {
703
- vx = this.topBox.intrinsicX * this.containerRect.width + 20;
704
-
697
+ let pxX = stageRect.x + 20;
698
+ if (topBox) {
699
+ const pxPreferredX = topBox.pxIntrinsicCoord.x + 20;
700
+ const pxIntrinsicWidth = width * stageRect.width;
705
701
  if (
706
- vx >
707
- this.containerRect.width - width * this.containerRect.width
702
+ pxPreferredX + pxIntrinsicWidth <=
703
+ stageRect.x + stageRect.width
708
704
  ) {
709
- vx = 20;
705
+ pxX = pxPreferredX;
710
706
  }
711
707
  }
712
- x = vx / this.containerRect.width;
708
+ x = (pxX - stageRect.x) / stageRect.width;
713
709
  }
714
710
 
715
711
  if (y == null) {
716
- let vy = 20;
717
-
718
- if (this.topBox) {
719
- vy = this.topBox.intrinsicY * this.containerRect.height + 20;
720
-
712
+ let pxY = stageRect.y + 20;
713
+ if (topBox) {
714
+ const pxPreferredY = topBox.pxIntrinsicCoord.y + 20;
715
+ const pxIntrinsicHeight = height * stageRect.height;
721
716
  if (
722
- vy >
723
- this.containerRect.height -
724
- height * this.containerRect.height
717
+ pxPreferredY + pxIntrinsicHeight <=
718
+ stageRect.y + stageRect.height
725
719
  ) {
726
- vy = 20;
720
+ pxY = pxPreferredY;
727
721
  }
728
722
  }
729
-
730
- y = vy / this.containerRect.height;
723
+ y = (pxY - stageRect.y) / stageRect.height;
731
724
  }
732
725
 
733
- return { ...config, x, y, width, height };
726
+ return { x, y, width, height };
734
727
  }
735
728
 
736
729
  protected makeBoxTop(box: TeleBox, skipUpdate = false): void {
737
730
  if (this.topBox) {
738
731
  if (box !== this.topBox) {
739
- box.setZIndex(this.topBox.zIndex + 1, skipUpdate);
732
+ box._zIndex$.setValue(this.topBox.zIndex + 1, skipUpdate);
740
733
  }
741
734
  }
742
735
  }