@netless/telebox-insider 1.0.0-alpha.2 → 1.0.0-alpha.22

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