@netless/telebox-insider 0.2.26 → 1.0.0-alpha.3

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