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

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 (38) hide show
  1. package/dist/TeleBox/index.d.ts +53 -35
  2. package/dist/TeleBox/typings.d.ts +22 -10
  3. package/dist/TeleBox/utils.d.ts +5 -0
  4. package/dist/TeleBoxCollector/index.d.ts +7 -8
  5. package/dist/TeleBoxManager/MaxTitleBar/index.d.ts +1 -1
  6. package/dist/TeleBoxManager/index.d.ts +15 -5
  7. package/dist/TeleBoxManager/typings.d.ts +50 -6
  8. package/dist/TeleTitleBar/DefaultTitleBar/index.d.ts +4 -12
  9. package/dist/TeleTitleBar/typings.d.ts +2 -2
  10. package/dist/index.d.ts +0 -1
  11. package/dist/style.css +650 -1
  12. package/dist/telebox-insider.cjs.js +1647 -1
  13. package/dist/telebox-insider.cjs.js.map +1 -1
  14. package/dist/telebox-insider.es.js +1629 -1
  15. package/dist/telebox-insider.es.js.map +1 -1
  16. package/dist/utils.d.ts +0 -1
  17. package/package.json +3 -3
  18. package/src/TeleBox/index.ts +403 -290
  19. package/src/TeleBox/style.scss +13 -25
  20. package/src/TeleBox/style.shadow.scss +77 -0
  21. package/src/TeleBox/typings.ts +22 -10
  22. package/src/TeleBox/utils.ts +32 -0
  23. package/src/TeleBoxCollector/index.ts +41 -38
  24. package/src/TeleBoxCollector/style.scss +6 -4
  25. package/src/TeleBoxManager/MaxTitleBar/index.ts +3 -28
  26. package/src/TeleBoxManager/MaxTitleBar/style.scss +18 -16
  27. package/src/TeleBoxManager/index.ts +184 -77
  28. package/src/TeleBoxManager/style.scss +55 -0
  29. package/src/TeleBoxManager/typings.ts +69 -7
  30. package/src/TeleTitleBar/DefaultTitleBar/index.ts +47 -61
  31. package/src/TeleTitleBar/DefaultTitleBar/style.scss +30 -10
  32. package/src/TeleTitleBar/typings.ts +2 -2
  33. package/src/index.ts +0 -2
  34. package/src/scss/variables.scss +72 -0
  35. package/src/utils.ts +0 -6
  36. package/dist/TeleStage/index.d.ts +0 -21
  37. package/src/TeleStage/index.ts +0 -178
  38. package/src/TeleStage/style.scss +0 -49
@@ -1,4 +1,5 @@
1
1
  import "./style.scss";
2
+ import shadowStyles from "./style.shadow.scss?inline";
2
3
 
3
4
  import Emittery from "emittery";
4
5
  import styler from "stylefire";
@@ -9,19 +10,17 @@ import type {
9
10
  ReadonlyValEnhancedResult,
10
11
  ValEnhancedResult,
11
12
  } from "value-enhancer";
12
- import { withValueEnhancer } from "value-enhancer";
13
- import { combine, Val, withReadonlyValueEnhancer } from "value-enhancer";
13
+ import {
14
+ combine,
15
+ Val,
16
+ withReadonlyValueEnhancer,
17
+ withValueEnhancer,
18
+ ValManager,
19
+ } from "value-enhancer";
14
20
  import type { TeleTitleBar } from "../TeleTitleBar";
15
21
  import { DefaultTitleBar } from "../TeleTitleBar";
16
22
  import { ResizeObserver as ResizeObserverPolyfill } from "@juggle/resize-observer";
17
- import {
18
- clamp,
19
- flattenEvent,
20
- getBoxDefaultName,
21
- isFalsy,
22
- isTruthy,
23
- preventEvent,
24
- } from "../utils";
23
+ import { clamp, getBoxDefaultName, preventEvent } from "../utils";
25
24
  import {
26
25
  TELE_BOX_EVENT,
27
26
  TELE_BOX_STATE,
@@ -38,7 +37,7 @@ import type {
38
37
  TeleBoxDelegateEventConfig,
39
38
  TeleBoxRect,
40
39
  } from "./typings";
41
- import { TeleStage } from "../TeleStage";
40
+ import { calcStageRect } from "./utils";
42
41
 
43
42
  export * from "./constants";
44
43
  export * from "./typings";
@@ -52,10 +51,13 @@ type ValConfig = {
52
51
  visible: Val<RequiredTeleBoxConfig["visible"], boolean>;
53
52
  resizable: Val<RequiredTeleBoxConfig["resizable"], boolean>;
54
53
  draggable: Val<RequiredTeleBoxConfig["draggable"], boolean>;
55
- ratio: Val<RequiredTeleBoxConfig["ratio"], boolean>;
54
+ boxRatio: Val<RequiredTeleBoxConfig["boxRatio"], boolean>;
55
+ stageRatio: Val<RequiredTeleBoxConfig["stageRatio"], boolean>;
56
+ stageStyle: Val<RequiredTeleBoxConfig["stageStyle"], boolean>;
57
+ bodyStyle: Val<RequiredTeleBoxConfig["bodyStyle"], boolean>;
56
58
  };
57
59
 
58
- type ReadonlyValConfig = {
60
+ type PropsValConfig = {
59
61
  darkMode: RequiredTeleBoxConfig["darkMode$"];
60
62
  fence: RequiredTeleBoxConfig["fence$"];
61
63
  minimized: RequiredTeleBoxConfig["minimized$"];
@@ -63,15 +65,16 @@ type ReadonlyValConfig = {
63
65
  readonly: RequiredTeleBoxConfig["readonly$"];
64
66
  rootRect: RequiredTeleBoxConfig["rootRect$"];
65
67
  managerStageRect: RequiredTeleBoxConfig["managerStageRect$"];
68
+ managerStageRatio: RequiredTeleBoxConfig["managerStageRatio$"];
69
+ defaultBoxStageStyle: RequiredTeleBoxConfig["defaultBoxStageStyle$"];
70
+ defaultBoxBodyStyle: RequiredTeleBoxConfig["defaultBoxBodyStyle$"];
66
71
  collectorRect: RequiredTeleBoxConfig["collectorRect$"];
72
+ };
67
73
 
74
+ type MyReadonlyValConfig = {
68
75
  zIndex: Val<RequiredTeleBoxConfig["zIndex"], boolean>;
69
76
  focus: Val<RequiredTeleBoxConfig["focus"], boolean>;
70
77
 
71
- $userContent: Val<TeleBoxConfig["content"], boolean>;
72
- $userFooter: Val<TeleBoxConfig["footer"], boolean>;
73
- $userStyles: Val<TeleBoxConfig["styles"], boolean>;
74
-
75
78
  minSize: Val<TeleBoxSize, boolean>;
76
79
  intrinsicSize: Val<TeleBoxSize, boolean>;
77
80
  intrinsicCoord: Val<TeleBoxCoord, boolean>;
@@ -79,13 +82,13 @@ type ReadonlyValConfig = {
79
82
  pxIntrinsicSize: ReadonlyVal<TeleBoxSize, boolean>;
80
83
  pxIntrinsicCoord: ReadonlyVal<TeleBoxCoord, boolean>;
81
84
  state: ReadonlyVal<TeleBoxState, boolean>;
82
- highlightStage: ReadonlyVal<boolean, boolean>;
83
- boxHighlightStage: Val<boolean | null, boolean>;
84
- contentRect: Val<TeleBoxRect>;
85
- contentStageRect: ReadonlyVal<TeleBoxRect | null>;
85
+ bodyRect: Val<TeleBoxRect>;
86
+ stageRect: ReadonlyVal<TeleBoxRect>;
86
87
  };
87
88
 
88
- type CombinedValEnhancedResult = ReadonlyValEnhancedResult<ReadonlyValConfig> &
89
+ type CombinedValEnhancedResult = ReadonlyValEnhancedResult<
90
+ PropsValConfig & MyReadonlyValConfig
91
+ > &
89
92
  ValEnhancedResult<ValConfig>;
90
93
 
91
94
  export interface TeleBox extends CombinedValEnhancedResult {}
@@ -104,41 +107,48 @@ export class TeleBox {
104
107
  y = 0.1,
105
108
  resizable = true,
106
109
  draggable = true,
107
- ratio = -1,
110
+ boxRatio = -1,
108
111
  focus = false,
109
112
  zIndex = 100,
113
+ stageRatio = null,
114
+ enableShadowDOM = true,
110
115
  titleBar,
111
116
  content,
117
+ stage,
112
118
  footer,
113
119
  styles,
114
- highlightStage = null,
120
+ userStyles,
121
+ bodyStyle = null,
122
+ stageStyle = null,
115
123
  darkMode$,
116
124
  fence$,
117
125
  minimized$,
118
126
  maximized$,
119
127
  readonly$,
120
- root$,
128
+ root,
121
129
  rootRect$,
122
130
  managerStageRect$,
123
- stageRatio$,
131
+ managerStageRatio$,
132
+ defaultBoxBodyStyle$,
133
+ defaultBoxStageStyle$,
124
134
  collectorRect$,
125
- managerHighlightStage$,
126
135
  }: TeleBoxConfig) {
127
136
  this._sideEffect = new SideEffectManager();
128
137
 
129
138
  this.id = id;
130
139
  this.namespace = namespace;
140
+ this.enableShadowDOM = enableShadowDOM;
141
+
142
+ const valManager = new ValManager();
143
+ this._sideEffect.addDisposer(() => valManager.destroy());
131
144
 
132
145
  const title$ = new Val(title);
133
146
  const visible$ = new Val(visible);
134
147
  const resizable$ = new Val(resizable);
135
148
  const draggable$ = new Val(draggable);
136
- const ratio$ = new Val(ratio);
149
+ const boxRatio$ = new Val(boxRatio);
137
150
  const zIndex$ = new Val(zIndex);
138
151
  const focus$ = new Val(focus);
139
- const $userContent$ = new Val(content);
140
- const $userFooter$ = new Val(footer);
141
- const $userStyles$ = new Val(styles);
142
152
 
143
153
  const state$ = combine(
144
154
  [minimized$, maximized$],
@@ -199,44 +209,31 @@ export class TeleBox {
199
209
  const pxIntrinsicCoord$ = combine(
200
210
  [intrinsicCoord$, managerStageRect$],
201
211
  ([intrinsicCoord, managerStageRect]) => ({
202
- x:
203
- intrinsicCoord.x * managerStageRect.width +
204
- managerStageRect.x,
205
- y:
206
- intrinsicCoord.y * managerStageRect.height +
207
- managerStageRect.y,
212
+ x: intrinsicCoord.x * managerStageRect.width,
213
+ y: intrinsicCoord.y * managerStageRect.height,
208
214
  }),
209
215
  { compare: shallowequal }
210
216
  );
211
217
 
212
- const boxHighlightStage$ = new Val<boolean | null, boolean>(
213
- highlightStage
214
- );
215
-
216
- const highlightStage$ = combine(
217
- [maximized$, boxHighlightStage$, managerHighlightStage$],
218
- ([maximized, boxHighlightStage, managerHighlightStage]) =>
219
- boxHighlightStage === null
220
- ? managerHighlightStage && maximized
221
- : boxHighlightStage
222
- );
218
+ const bodyStyle$ = new Val<string | null>(bodyStyle);
219
+ const stageStyle$ = new Val<string | null>(stageStyle);
223
220
 
224
221
  const contentRoot$ = new Val<HTMLElement | null>(null);
225
-
226
- const contentRect$ = new Val<TeleBoxRect>(rootRect$.value, {
222
+ const bodyRect$ = new Val<TeleBoxRect>(managerStageRect$.value, {
227
223
  compare: shallowequal,
228
224
  });
225
+ const stageRatio$ = new Val(stageRatio);
226
+ const finalStageRatio$ = combine(
227
+ [stageRatio$, managerStageRatio$],
228
+ ([stageRatio, managerStageRatio]) => stageRatio ?? managerStageRatio
229
+ );
230
+ const stageRect$ = combine(
231
+ [bodyRect$, finalStageRatio$],
232
+ calcStageRect,
233
+ { compare: shallowequal }
234
+ );
229
235
 
230
- const teleStage = new TeleStage({
231
- namespace,
232
- root$: contentRoot$,
233
- rootRect$: contentRect$,
234
- ratio$: stageRatio$,
235
- highlightStage$,
236
- });
237
- this._sideEffect.addDisposer(() => teleStage.destroy());
238
-
239
- const readonlyValConfig: ReadonlyValConfig = {
236
+ const propsValConfig: PropsValConfig = {
240
237
  darkMode: darkMode$,
241
238
  fence: fence$,
242
239
  minimized: minimized$,
@@ -244,15 +241,18 @@ export class TeleBox {
244
241
  readonly: readonly$,
245
242
  rootRect: rootRect$,
246
243
  managerStageRect: managerStageRect$,
244
+ managerStageRatio: managerStageRatio$,
245
+ defaultBoxBodyStyle: defaultBoxBodyStyle$,
246
+ defaultBoxStageStyle: defaultBoxStageStyle$,
247
247
  collectorRect: collectorRect$,
248
+ };
248
249
 
250
+ withReadonlyValueEnhancer(this, propsValConfig);
251
+
252
+ const myReadonlyValConfig: MyReadonlyValConfig = {
249
253
  zIndex: zIndex$,
250
254
  focus: focus$,
251
255
 
252
- $userContent: $userContent$,
253
- $userFooter: $userFooter$,
254
- $userStyles: $userStyles$,
255
-
256
256
  state: state$,
257
257
  minSize: minSize$,
258
258
  pxMinSize: pxMinSize$,
@@ -260,44 +260,44 @@ export class TeleBox {
260
260
  intrinsicCoord: intrinsicCoord$,
261
261
  pxIntrinsicSize: pxIntrinsicSize$,
262
262
  pxIntrinsicCoord: pxIntrinsicCoord$,
263
- highlightStage: highlightStage$,
264
- boxHighlightStage: boxHighlightStage$,
265
- contentRect: contentRect$,
266
- contentStageRect: teleStage.stageRect$,
263
+ bodyRect: bodyRect$,
264
+ stageRect: stageRect$,
267
265
  };
268
266
 
269
- withReadonlyValueEnhancer(this, readonlyValConfig);
267
+ withReadonlyValueEnhancer(this, myReadonlyValConfig, valManager);
270
268
 
271
269
  const valConfig: ValConfig = {
272
270
  title: title$,
273
271
  visible: visible$,
274
272
  resizable: resizable$,
275
273
  draggable: draggable$,
276
- ratio: ratio$,
274
+ boxRatio: boxRatio$,
275
+ stageRatio: stageRatio$,
276
+ bodyStyle: bodyStyle$,
277
+ stageStyle: stageStyle$,
277
278
  };
278
279
 
279
- withValueEnhancer(this, valConfig);
280
+ withValueEnhancer(this, valConfig, valManager);
280
281
 
281
282
  this.titleBar =
282
283
  titleBar ||
283
284
  new DefaultTitleBar({
284
285
  readonly$: readonly$,
285
286
  state$: state$,
286
- title: title$.value,
287
+ title$: title$,
287
288
  namespace: this.namespace,
288
289
  onDragStart: (event) => this._handleTrackStart?.(event),
289
290
  onEvent: (event) => this._delegateEvents.emit(event.type),
290
291
  });
291
292
 
292
293
  this._sideEffect.addDisposer(
293
- ratio$.subscribe((ratio) => {
294
- if (ratio > 0) {
294
+ boxRatio$.subscribe((boxRatio) => {
295
+ if (boxRatio > 0) {
295
296
  this.transform(
296
297
  pxIntrinsicCoord$.value.x,
297
298
  pxIntrinsicCoord$.value.y,
298
299
  pxIntrinsicSize$.value.width,
299
- pxIntrinsicSize$.value.height,
300
- true
300
+ pxIntrinsicSize$.value.height
301
301
  );
302
302
  }
303
303
  })
@@ -308,15 +308,20 @@ export class TeleBox {
308
308
  if (fence) {
309
309
  this.move(
310
310
  pxIntrinsicCoord$.value.x,
311
- pxIntrinsicCoord$.value.y,
312
- true
311
+ pxIntrinsicCoord$.value.y
313
312
  );
314
313
  }
315
314
  })
316
315
  );
317
316
 
318
- this.$box = this.render();
317
+ this.$box = this._render();
319
318
  contentRoot$.setValue(this.$content.parentElement);
319
+ content && this.mountContent(content);
320
+ stage && this.mountStage(stage);
321
+ footer && this.mountFooter(footer);
322
+ styles && this.mountStyles(styles);
323
+ userStyles && this.mountUserStyles(userStyles);
324
+ root.appendChild(this.$box);
320
325
 
321
326
  const watchValEvent = <E extends TeleBoxEvent>(
322
327
  val: ReadonlyVal<TeleBoxEventConfig[E], boolean>,
@@ -353,13 +358,6 @@ export class TeleBox {
353
358
  );
354
359
  }
355
360
  }),
356
- root$.subscribe((root) => {
357
- if (root) {
358
- root.appendChild(this.$box);
359
- } else if (this.$box.parentNode) {
360
- this.$box.remove();
361
- }
362
- }),
363
361
  ]);
364
362
  }
365
363
 
@@ -368,6 +366,9 @@ export class TeleBox {
368
366
  /** ClassName Prefix. For CSS styling. Default "telebox" */
369
367
  public readonly namespace: string;
370
368
 
369
+ /** Enable shadow DOM for box content. Default true. */
370
+ public readonly enableShadowDOM: boolean;
371
+
371
372
  public readonly events = new Emittery<
372
373
  TeleBoxEventConfig,
373
374
  TeleBoxEventConfig
@@ -465,34 +466,25 @@ export class TeleBox {
465
466
  const pxIntrinsicSize = this.pxIntrinsicSize;
466
467
 
467
468
  if (this.fence) {
468
- safeX = clamp(
469
- x,
470
- managerStageRect.x,
471
- managerStageRect.x +
472
- managerStageRect.width -
473
- pxIntrinsicSize.width
474
- );
469
+ safeX = clamp(x, 0, managerStageRect.width - pxIntrinsicSize.width);
475
470
  safeY = clamp(
476
471
  y,
477
- managerStageRect.y,
478
- managerStageRect.y +
479
- managerStageRect.height -
480
- pxIntrinsicSize.height
472
+ 0,
473
+ managerStageRect.height - pxIntrinsicSize.height
481
474
  );
482
475
  } else {
483
- const rootRect = this.rootRect;
484
476
  safeX = clamp(
485
477
  x,
486
- rootRect.x - pxIntrinsicSize.width + 20,
487
- rootRect.x + rootRect.width - 20
478
+ -(pxIntrinsicSize.width - 120),
479
+ 0 + managerStageRect.width - 20
488
480
  );
489
- safeY = clamp(y, rootRect.y, rootRect.y + rootRect.height - 20);
481
+ safeY = clamp(y, 0, 0 + managerStageRect.height - 20);
490
482
  }
491
483
 
492
484
  this._intrinsicCoord$.setValue(
493
485
  {
494
- x: (safeX - managerStageRect.x) / managerStageRect.width,
495
- y: (safeY - managerStageRect.y) / managerStageRect.height,
486
+ x: safeX / managerStageRect.width,
487
+ y: safeY / managerStageRect.height,
496
488
  },
497
489
  skipUpdate
498
490
  );
@@ -515,21 +507,20 @@ export class TeleBox {
515
507
  skipUpdate = false
516
508
  ): void {
517
509
  const managerStageRect = this.managerStageRect;
518
- const rootRect = this.rootRect;
519
510
 
520
511
  width = Math.max(width, this.pxMinSize.width);
521
512
  height = Math.max(height, this.pxMinSize.height);
522
513
 
523
- if (this.ratio > 0) {
524
- const newHeight = this.ratio * width;
514
+ if (this.boxRatio > 0) {
515
+ const newHeight = this.boxRatio * width;
525
516
  if (y !== this.pxIntrinsicCoord.y) {
526
517
  y -= newHeight - height;
527
518
  }
528
519
  height = newHeight;
529
520
  }
530
521
 
531
- if (y < rootRect.y) {
532
- y = rootRect.y;
522
+ if (y < 0) {
523
+ y = 0;
533
524
  height = this.pxIntrinsicSize.height;
534
525
  }
535
526
 
@@ -543,104 +534,158 @@ export class TeleBox {
543
534
  );
544
535
  }
545
536
 
546
- /**
547
- * Mount dom to box content.
548
- */
537
+ private $authorContent?: HTMLElement;
538
+
539
+ /** Mount dom to box content. */
549
540
  public mountContent(content: HTMLElement): void {
550
- this._$userContent$.setValue(content);
541
+ this.$authorContent?.remove();
542
+ this.$authorContent = content;
543
+ this.$content.appendChild(content);
551
544
  }
552
545
 
553
- /**
554
- * Unmount content from the box.
555
- */
546
+ /** Unmount content from the box. */
556
547
  public unmountContent(): void {
557
- this._$userContent$.setValue(undefined);
548
+ if (this.$authorContent) {
549
+ this.$authorContent.remove();
550
+ this.$authorContent = undefined;
551
+ }
558
552
  }
559
553
 
560
- /**
561
- * Mount dom to box Footer.
562
- */
563
- public mountFooter(footer: HTMLElement): void {
564
- this._$userFooter$.setValue(footer);
554
+ private $authorStage?: HTMLElement;
555
+
556
+ /** Mount dom to box stage. */
557
+ public mountStage(stage: HTMLElement): void {
558
+ this.$authorStage?.remove();
559
+ this.$authorStage = stage;
560
+ if (!this.$stage) {
561
+ this.$stage = this._renderStage();
562
+ }
563
+ this.$stage.appendChild(stage);
564
+ if (!this.$stage.parentElement) {
565
+ this.$body.appendChild(this.$stage);
566
+ }
565
567
  }
566
568
 
567
- /**
568
- * Unmount Footer from the box.
569
- */
570
- public unmountFooter(): void {
571
- this._$userFooter$.setValue(undefined);
569
+ /** Unmount content from the box. */
570
+ public unmountStage(): void {
571
+ if (this.$authorStage) {
572
+ this.$authorStage.remove();
573
+ this.$authorStage = undefined;
574
+ }
575
+ this.$stage?.remove();
572
576
  }
573
577
 
574
- public getUserStyles(): HTMLStyleElement | undefined {
575
- return this.$userStyles;
578
+ private $authorFooter?: HTMLElement;
579
+
580
+ /** Mount dom to box Footer. */
581
+ public mountFooter(footer: HTMLElement): void {
582
+ this.$authorFooter?.remove();
583
+ this.$authorFooter = footer;
584
+ this.$footer.appendChild(footer);
576
585
  }
577
586
 
578
- public mountStyles(styles: string | HTMLStyleElement): void {
579
- let $styles: HTMLStyleElement;
580
- if (typeof styles === "string") {
581
- $styles = document.createElement("style");
582
- $styles.textContent = styles;
583
- } else {
584
- $styles = styles;
587
+ /** Unmount Footer from the box. */
588
+ public unmountFooter(): void {
589
+ if (this.$authorFooter) {
590
+ this.$authorFooter.remove();
591
+ this.$authorFooter = undefined;
585
592
  }
586
- this._$userStyles$.setValue($styles);
587
593
  }
588
594
 
595
+ /** Mount styles for box content */
596
+ public mountStyles(styles: string): void {
597
+ this.$styles.textContent = styles;
598
+ }
599
+
600
+ /** Umount styles for box content */
589
601
  public unmountStyles(): void {
590
- this._$userStyles$.setValue(undefined);
602
+ this.$styles.textContent = "";
603
+ }
604
+
605
+ /** Mount user styles for box content */
606
+ public mountUserStyles(styles: string): void {
607
+ this.$userStyles.textContent = styles;
591
608
  }
592
609
 
593
- /** Show stage frame and grey-out the non-stage area. Inherit setting from manager if null. */
594
- setHighlightStage(highlightStage: boolean | null): void {
595
- this._boxHighlightStage$.setValue(highlightStage);
610
+ /** Umount user styles for box content */
611
+ public unmountUserStyles(): void {
612
+ this.$userStyles.textContent = "";
596
613
  }
597
614
 
598
615
  /** DOM of the box */
599
616
  public $box: HTMLElement;
600
617
 
601
- /** DOM of the box content */
618
+ /** DOM of main area of the box. Including $body and $footer. */
619
+ public $main!: HTMLElement;
620
+
621
+ /** DOM of the box body */
622
+ public $body!: HTMLElement;
623
+
624
+ /** DOM of the box content container inside box body */
602
625
  public $content!: HTMLElement;
603
626
 
604
- /** DOM of the box title bar */
627
+ /** DOM of the box stage area inside box body */
628
+ public $stage?: HTMLElement;
629
+
630
+ /** DOM of custom box content styles */
631
+ public $styles!: HTMLStyleElement;
632
+
633
+ /** DOM of end user custom box content styles */
634
+ public $userStyles!: HTMLStyleElement;
635
+
636
+ /** DOM of the box title bar container */
605
637
  public $titleBar!: HTMLElement;
606
638
 
607
- /** DOM of the box footer */
639
+ /** DOM of the box footer container */
608
640
  public $footer!: HTMLElement;
609
641
 
610
- protected render(): HTMLElement {
642
+ private _render(): HTMLElement {
611
643
  if (this.$box) {
612
644
  return this.$box;
613
645
  }
614
646
 
615
- this.$box = document.createElement("div");
616
-
617
- this.$box.classList.add(this.wrapClassName("box"));
618
-
619
- const bindClassName = <TValue>(
620
- el: Element,
621
- val: ReadonlyVal<TValue, boolean>,
622
- className: string,
623
- predicate: (value: TValue) => boolean = isTruthy
624
- ): string => {
625
- return this._sideEffect.add(() => {
626
- const wrappedClassName = this.wrapClassName(className);
627
- return val.subscribe((value) => {
628
- el.classList.toggle(wrappedClassName, predicate(value));
629
- });
630
- });
647
+ const bindBoxStates = (el: Element, disposerID?: string): string => {
648
+ return this._sideEffect.addDisposer(
649
+ [
650
+ this._readonly$.subscribe((readonly) =>
651
+ el.classList.toggle(
652
+ this.wrapClassName("readonly"),
653
+ readonly
654
+ )
655
+ ),
656
+ this._draggable$.subscribe((draggable) =>
657
+ el.classList.toggle(
658
+ this.wrapClassName("no-drag"),
659
+ !draggable
660
+ )
661
+ ),
662
+ this._resizable$.subscribe((resizable) =>
663
+ el.classList.toggle(
664
+ this.wrapClassName("no-resize"),
665
+ !resizable
666
+ )
667
+ ),
668
+ this._focus$.subscribe((focus) =>
669
+ el.classList.toggle(this.wrapClassName("blur"), !focus)
670
+ ),
671
+ this._darkMode$.subscribe((darkMode) => {
672
+ el.classList.toggle(
673
+ this.wrapClassName("color-scheme-dark"),
674
+ darkMode
675
+ );
676
+ el.classList.toggle(
677
+ this.wrapClassName("color-scheme-light"),
678
+ !darkMode
679
+ );
680
+ }),
681
+ ],
682
+ disposerID
683
+ );
631
684
  };
632
685
 
633
- bindClassName(this.$box, this._readonly$, "readonly");
634
- bindClassName(this.$box, this._draggable$, "no-drag", isFalsy);
635
- bindClassName(this.$box, this._resizable$, "no-resize", isFalsy);
636
- bindClassName(this.$box, this._focus$, "blur", isFalsy);
637
- bindClassName(this.$box, this._darkMode$, "color-scheme-dark");
638
- bindClassName(
639
- this.$box,
640
- this._darkMode$,
641
- "color-scheme-light",
642
- isFalsy
643
- );
686
+ this.$box = document.createElement("div");
687
+ this.$box.classList.add(this.wrapClassName("box"));
688
+ bindBoxStates(this.$box, "bind-box-state");
644
689
 
645
690
  this._sideEffect.add(() => {
646
691
  const minimizedClassName = this.wrapClassName("minimized");
@@ -695,6 +740,8 @@ export class TeleBox {
695
740
  this._pxIntrinsicSize$,
696
741
  this._pxIntrinsicCoord$,
697
742
  this._collectorRect$,
743
+ this._rootRect$,
744
+ this._managerStageRect$,
698
745
  ],
699
746
  ([
700
747
  maximized,
@@ -702,42 +749,58 @@ export class TeleBox {
702
749
  pxIntrinsicSize,
703
750
  pxIntrinsicCoord,
704
751
  collectorRect,
752
+ rootRect,
753
+ managerStageRect,
705
754
  ]) => {
755
+ const styles: {
756
+ x: number;
757
+ y: number;
758
+ width: number;
759
+ height: number;
760
+ scaleX: number;
761
+ scaleY: number;
762
+ } = maximized
763
+ ? {
764
+ x: -managerStageRect.x,
765
+ y: -managerStageRect.y,
766
+ width: rootRect.width,
767
+ height: rootRect.height,
768
+ scaleX: 1,
769
+ scaleY: 1,
770
+ }
771
+ : {
772
+ x: pxIntrinsicCoord.x,
773
+ y: pxIntrinsicCoord.y,
774
+ width: pxIntrinsicSize.width,
775
+ height: pxIntrinsicSize.height,
776
+ scaleX: 1,
777
+ scaleY: 1,
778
+ };
706
779
  if (minimized && collectorRect) {
707
- return {
708
- x: collectorRect.x + collectorRect.width / 2,
709
- y: collectorRect.y + collectorRect.height / 2,
710
- width: pxIntrinsicSize.width + "px",
711
- height: pxIntrinsicSize.height + "px",
712
- scaleX: collectorRect.width / pxIntrinsicSize.width,
713
- scaleY: collectorRect.height / pxIntrinsicSize.height,
714
- };
715
- }
716
- if (maximized) {
717
- return {
718
- x: 0,
719
- y: 0,
720
- width: "100%",
721
- height: "100%",
722
- scaleX: 1,
723
- scaleY: 1,
724
- };
780
+ const { width: boxWidth, height: boxHeight } = maximized
781
+ ? this.rootRect
782
+ : pxIntrinsicSize;
783
+ styles.x =
784
+ collectorRect.x -
785
+ boxWidth / 2 +
786
+ collectorRect.width / 2 -
787
+ managerStageRect.x;
788
+ styles.y =
789
+ collectorRect.y -
790
+ boxHeight / 2 +
791
+ collectorRect.height / 2 -
792
+ managerStageRect.y;
793
+ styles.scaleX = collectorRect.width / boxWidth;
794
+ styles.scaleY = collectorRect.height / boxHeight;
725
795
  }
726
- return {
727
- x: pxIntrinsicCoord.x,
728
- y: pxIntrinsicCoord.y,
729
- width: pxIntrinsicSize.width + "px",
730
- height: pxIntrinsicSize.height + "px",
731
- scaleX: 1,
732
- scaleY: 1,
733
- };
796
+ return styles;
734
797
  },
735
798
  { compare: shallowequal }
736
799
  );
737
800
 
738
801
  const boxStyles = boxStyles$.value;
739
- this.$box.style.width = boxStyles.width;
740
- this.$box.style.height = boxStyles.height;
802
+ this.$box.style.width = boxStyles.width + "px";
803
+ this.$box.style.height = boxStyles.height + "px";
741
804
  // Add 10px offset on first frame
742
805
  // which creates a subtle moving effect
743
806
  this.$box.style.transform = `translate(${boxStyles.x - 10}px,${
@@ -759,91 +822,133 @@ export class TeleBox {
759
822
  $titleBar.appendChild(this.titleBar.render());
760
823
  this.$titleBar = $titleBar;
761
824
 
762
- const $contentWrap = document.createElement("div");
763
- $contentWrap.className = this.wrapClassName("content-wrap");
825
+ const $body = document.createElement("div");
826
+ $body.className = this.wrapClassName("body-wrap");
827
+ this.$body = $body;
828
+
829
+ const $styles = document.createElement("style");
830
+ this.$styles = $styles;
831
+ $body.appendChild($styles);
832
+
833
+ const $userStyles = document.createElement("style");
834
+ this.$userStyles = $userStyles;
835
+ $body.appendChild($userStyles);
764
836
 
765
837
  const $content = document.createElement("div");
766
838
  $content.className =
767
839
  this.wrapClassName("content") + " tele-fancy-scrollbar";
768
840
  this.$content = $content;
841
+ this._sideEffect.addDisposer(
842
+ combine(
843
+ [this._bodyStyle$, this._defaultBoxBodyStyle$],
844
+ ([bodyStyle, defaultBoxBodyStyle]) =>
845
+ bodyStyle ?? defaultBoxBodyStyle
846
+ ).subscribe((style) => ($content.style.cssText = style || ""))
847
+ );
769
848
 
770
- this._sideEffect.add(() => {
771
- const observer = new ResizeObserver((entries) => {
772
- const rect = entries[0]?.contentRect;
773
- if (rect) {
774
- this._contentRect$.setValue({
775
- x: rect.x,
776
- y: rect.y,
777
- width: rect.width,
778
- height: rect.height,
779
- });
780
- }
781
- });
782
- observer.observe($content);
783
- return () => observer.disconnect();
784
- });
785
-
786
- this._sideEffect.add(() => {
787
- let last$userStyles: HTMLStyleElement | undefined;
788
- return this._$userStyles$.subscribe(($userStyles) => {
789
- if (last$userStyles) {
790
- last$userStyles.remove();
791
- }
792
- last$userStyles = $userStyles;
793
- if ($userStyles) {
794
- $contentWrap.appendChild($userStyles);
795
- }
796
- });
797
- });
798
-
799
- this._sideEffect.add(() => {
800
- let last$userContent: HTMLElement | undefined;
801
- return this._$userContent$.subscribe(($userContent) => {
802
- if (last$userContent) {
803
- last$userContent.remove();
804
- }
805
- last$userContent = $userContent;
806
- if ($userContent) {
807
- $content.appendChild($userContent);
808
- }
809
- });
810
- });
811
-
812
- $contentWrap.appendChild($content);
849
+ $body.appendChild($content);
813
850
 
814
851
  const $footer = document.createElement("div");
815
852
  $footer.className = this.wrapClassName("footer-wrap");
816
853
  this.$footer = $footer;
817
854
 
855
+ $boxMain.appendChild($titleBar);
856
+
857
+ const $main = document.createElement("div");
858
+ $main.className = this.wrapClassName("main");
859
+ this.$main = $main;
860
+ $boxMain.appendChild($main);
861
+
862
+ const $quarantineOuter = document.createElement("div");
863
+ $quarantineOuter.className = this.wrapClassName("quarantine-outer");
864
+ $main.appendChild($quarantineOuter);
865
+
866
+ const $quarantine = document.createElement("div");
867
+ $quarantine.className = this.wrapClassName("quarantine");
868
+ $quarantine.appendChild($body);
869
+ $quarantine.appendChild($footer);
870
+
871
+ if (this.enableShadowDOM) {
872
+ bindBoxStates($quarantine, "bind-quarantine-state");
873
+ const $shadowStyle = document.createElement("style");
874
+ $shadowStyle.textContent = shadowStyles;
875
+ $quarantine.insertBefore($shadowStyle, $quarantine.firstChild);
876
+ const shadow = $quarantineOuter.attachShadow({ mode: "open" });
877
+ shadow.appendChild($quarantine);
878
+ } else {
879
+ $quarantineOuter.appendChild($quarantine);
880
+ }
881
+
882
+ this._renderResizeHandlers();
883
+
884
+ const updateBodyRect = (): void => {
885
+ const rect = $body.getBoundingClientRect();
886
+ this._bodyRect$.setValue({
887
+ x: 0,
888
+ y: 0,
889
+ width: rect.width,
890
+ height: rect.height,
891
+ });
892
+ };
818
893
  this._sideEffect.add(() => {
819
- let last$userFooter: HTMLElement | undefined;
820
- return this._$userFooter$.subscribe(($userFooter) => {
821
- if (last$userFooter) {
822
- last$userFooter.remove();
823
- }
824
- last$userFooter = $userFooter;
825
- if ($userFooter) {
826
- $footer.appendChild($userFooter);
894
+ const observer = new ResizeObserver(() => {
895
+ if (!this.minimized) {
896
+ updateBodyRect();
827
897
  }
828
898
  });
899
+ observer.observe($body);
900
+ return () => observer.disconnect();
829
901
  });
902
+ this._sideEffect.addDisposer(
903
+ this._minimized$.reaction((minimized) => {
904
+ // correct content size when restoring from minimized
905
+ if (!minimized) {
906
+ this._sideEffect.setTimeout(
907
+ updateBodyRect,
908
+ 400,
909
+ "minimized-content-rect-fix"
910
+ );
911
+ }
912
+ })
913
+ );
830
914
 
831
- $boxMain.appendChild($titleBar);
832
- $boxMain.appendChild($contentWrap);
833
- $boxMain.appendChild($footer);
915
+ return this.$box;
916
+ }
834
917
 
835
- this._renderResizeHandlers();
918
+ private _renderStage(): HTMLDivElement {
919
+ const $stage = document.createElement("div");
836
920
 
837
- return this.$box;
921
+ $stage.className = this.wrapClassName("box-stage");
922
+ const updateStageRect = (stageRect: TeleBoxRect): void => {
923
+ $stage.style.top = stageRect.y + "px";
924
+ $stage.style.left = stageRect.x + "px";
925
+ $stage.style.width = stageRect.width + "px";
926
+ $stage.style.height = stageRect.height + "px";
927
+ };
928
+ this._sideEffect.addDisposer(
929
+ [
930
+ combine(
931
+ [this._stageStyle$, this._defaultBoxStageStyle$],
932
+ ([stageStyle, defaultBoxStageStyle]) =>
933
+ stageStyle ?? defaultBoxStageStyle
934
+ ).subscribe((styles) => {
935
+ $stage.style.cssText = styles || "";
936
+ updateStageRect(this._stageRect$.value);
937
+ }),
938
+ this._stageRect$.subscribe(updateStageRect),
939
+ ],
940
+ "box-stage-styles"
941
+ );
942
+ return $stage;
838
943
  }
839
944
 
840
- protected _handleTrackStart?: (ev: MouseEvent | TouchEvent) => void;
945
+ private _handleTrackStart?: (ev: PointerEvent) => void;
841
946
 
842
- public handleTrackStart: (ev: MouseEvent | TouchEvent) => void = (ev) => {
947
+ public handleTrackStart: (ev: PointerEvent) => void = (ev) => {
843
948
  return this._handleTrackStart?.(ev);
844
949
  };
845
950
 
846
- protected _renderResizeHandlers(): void {
951
+ private _renderResizeHandlers(): void {
847
952
  const $resizeHandles = document.createElement("div");
848
953
  $resizeHandles.className = this.wrapClassName("resize-handles");
849
954
 
@@ -876,16 +981,16 @@ export class TeleBox {
876
981
 
877
982
  let trackingHandle: TeleBoxHandleType | undefined;
878
983
 
879
- const handleTracking = (ev: MouseEvent | TouchEvent): void => {
880
- if (this.state !== TELE_BOX_STATE.Normal) {
984
+ const handleTracking = (ev: PointerEvent): void => {
985
+ if (!ev.isPrimary || this.state !== TELE_BOX_STATE.Normal) {
881
986
  return;
882
987
  }
883
988
 
884
989
  preventEvent(ev);
885
990
 
886
- let { pageX, pageY } = flattenEvent(ev);
887
- if (pageY < this.rootRect.y) {
888
- pageY = this.rootRect.y;
991
+ let { pageX, pageY } = ev;
992
+ if (pageY < 0) {
993
+ pageY = 0;
889
994
  }
890
995
 
891
996
  const offsetX = pageX - trackStartPageX;
@@ -947,7 +1052,11 @@ export class TeleBox {
947
1052
  this.transform(newX, newY, newWidth, newHeight);
948
1053
  };
949
1054
 
950
- const handleTrackEnd = (ev: MouseEvent | TouchEvent): void => {
1055
+ const handleTrackEnd = (ev: PointerEvent): void => {
1056
+ if (!ev.isPrimary) {
1057
+ return;
1058
+ }
1059
+
951
1060
  trackingHandle = void 0;
952
1061
 
953
1062
  if (!$trackMask) {
@@ -963,8 +1072,8 @@ export class TeleBox {
963
1072
  $trackMask.remove();
964
1073
  };
965
1074
 
966
- const handleTrackStart = (ev: MouseEvent | TouchEvent): void => {
967
- if (this.readonly) {
1075
+ const handleTrackStart = (ev: PointerEvent): void => {
1076
+ if (!ev.isPrimary || this.readonly) {
968
1077
  return;
969
1078
  }
970
1079
 
@@ -992,8 +1101,7 @@ export class TeleBox {
992
1101
  ({ width: trackStartWidth, height: trackStartHeight } =
993
1102
  this.pxIntrinsicSize);
994
1103
 
995
- ({ pageX: trackStartPageX, pageY: trackStartPageY } =
996
- flattenEvent(ev));
1104
+ ({ pageX: trackStartPageX, pageY: trackStartPageY } = ev);
997
1105
 
998
1106
  trackingHandle = target.dataset
999
1107
  .teleBoxHandle as TELE_BOX_RESIZE_HANDLE;
@@ -1015,25 +1123,24 @@ export class TeleBox {
1015
1123
  this.$box.classList.add(transformingClassName);
1016
1124
 
1017
1125
  this._sideEffect.add(() => {
1018
- window.addEventListener("mousemove", handleTracking);
1019
- window.addEventListener("touchmove", handleTracking, {
1126
+ window.addEventListener("pointermove", handleTracking, {
1020
1127
  passive: false,
1021
1128
  });
1022
- window.addEventListener("mouseup", handleTrackEnd);
1023
- window.addEventListener("touchend", handleTrackEnd, {
1129
+ window.addEventListener("pointerup", handleTrackEnd, {
1024
1130
  passive: false,
1025
1131
  });
1026
- window.addEventListener("touchcancel", handleTrackEnd, {
1132
+ window.addEventListener("pointercancel", handleTrackEnd, {
1027
1133
  passive: false,
1028
1134
  });
1029
1135
 
1030
1136
  return () => {
1031
- window.removeEventListener("mousemove", handleTracking);
1032
- window.removeEventListener("touchmove", handleTracking);
1033
- window.removeEventListener("mouseup", handleTrackEnd);
1034
- window.removeEventListener("touchend", handleTrackEnd);
1035
1137
  window.removeEventListener(
1036
- "touchcancel",
1138
+ "pointermove",
1139
+ handleTracking
1140
+ );
1141
+ window.removeEventListener("pointerup", handleTrackEnd);
1142
+ window.removeEventListener(
1143
+ "pointercancel",
1037
1144
  handleTrackEnd
1038
1145
  );
1039
1146
  };
@@ -1045,18 +1152,10 @@ export class TeleBox {
1045
1152
 
1046
1153
  this._sideEffect.addEventListener(
1047
1154
  $resizeHandles,
1048
- "mousedown",
1155
+ "pointerdown",
1049
1156
  handleTrackStart,
1050
1157
  {},
1051
- "box-resizeHandles-mousedown"
1052
- );
1053
-
1054
- this._sideEffect.addEventListener(
1055
- $resizeHandles,
1056
- "touchstart",
1057
- handleTrackStart,
1058
- { passive: false },
1059
- "box-resizeHandles-touchstart"
1158
+ "box-resizeHandles-pointerdown"
1060
1159
  );
1061
1160
  }
1062
1161
 
@@ -1090,7 +1189,21 @@ export type ReadonlyTeleBox = Pick<
1090
1189
  | PropKeys
1091
1190
  | "wrapClassName"
1092
1191
  | "mountContent"
1192
+ | "unmountContent"
1093
1193
  | "mountFooter"
1194
+ | "unmountFooter"
1195
+ | "mountStage"
1196
+ | "unmountStage"
1094
1197
  | "mountStyles"
1198
+ | "unmountStyles"
1199
+ | "setTitle"
1200
+ | "setDraggable"
1201
+ | "setResizable"
1202
+ | "setVisible"
1203
+ | "setBoxRatio"
1204
+ | "setStageRatio"
1205
+ | "setStageStyle"
1206
+ | "setBodyStyle"
1095
1207
  | "handleTrackStart"
1208
+ | "onValChanged"
1096
1209
  >;