@netless/telebox-insider 0.2.8 → 0.2.12

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.
@@ -59,7 +59,6 @@ export class TeleBoxManager {
59
59
  },
60
60
  collector,
61
61
  namespace = "telebox",
62
- zIndex = 100,
63
62
  readonly = false,
64
63
  }: TeleBoxManagerConfig = {}) {
65
64
  this._sideEffect = new SideEffectManager();
@@ -67,18 +66,32 @@ export class TeleBoxManager {
67
66
 
68
67
  this.root = root;
69
68
  this.namespace = namespace;
70
- this.zIndex = zIndex;
71
69
 
72
- const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
73
- const prefersDark$ = createVal(prefersDark.matches);
74
- this._sideEffect.add(() => {
75
- const handler = (evt: MediaQueryListEvent): void => {
76
- prefersDark$.setValue(evt.matches);
77
- };
78
- prefersDark.addEventListener("change", handler);
79
- return () => prefersDark.removeEventListener("change", handler);
70
+ this.boxes$ = createVal<TeleBox[]>([]);
71
+ this.topBox$ = this.boxes$.derive((boxes) => {
72
+ if (boxes.length > 0) {
73
+ const topBox = boxes.reduce((topBox, box) =>
74
+ topBox.zIndex > box.zIndex ? topBox : box
75
+ );
76
+ return topBox;
77
+ }
78
+ return;
80
79
  });
81
80
 
81
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
82
+ const prefersDark$ = createVal(false);
83
+
84
+ if (prefersDark) {
85
+ prefersDark$.setValue(prefersDark.matches);
86
+ this._sideEffect.add(() => {
87
+ const handler = (evt: MediaQueryListEvent): void => {
88
+ prefersDark$.setValue(evt.matches);
89
+ };
90
+ prefersDark.addListener(handler);
91
+ return () => prefersDark.removeListener(handler);
92
+ });
93
+ }
94
+
82
95
  const prefersColorScheme$ = createVal(prefersColorScheme);
83
96
  prefersColorScheme$.reaction((prefersColorScheme, _, skipUpdate) => {
84
97
  this.boxes.forEach((box) =>
@@ -166,6 +179,7 @@ export class TeleBoxManager {
166
179
  if (collector) {
167
180
  collector.setVisible(minimized$.value);
168
181
  collector.setReadonly(readonly$.value);
182
+ collector.setDarkMode(this._darkMode$.value);
169
183
  this._sideEffect.add(() => {
170
184
  collector.onClick = () => {
171
185
  if (!readonly$.value) {
@@ -179,6 +193,9 @@ export class TeleBoxManager {
179
193
  readonly$.subscribe((readonly) =>
180
194
  collector$.value?.setReadonly(readonly)
181
195
  );
196
+ this._darkMode$.subscribe((darkMode) => {
197
+ collector$.value?.setDarkMode(darkMode);
198
+ });
182
199
 
183
200
  minimized$.subscribe((minimized, _, skipUpdate) => {
184
201
  collector$.value?.setVisible(minimized);
@@ -213,6 +230,8 @@ export class TeleBoxManager {
213
230
  }
214
231
  });
215
232
 
233
+ const closeBtnClassName = this.wrapClassName("titlebar-icon-close");
234
+
216
235
  const checkFocusBox = (ev: MouseEvent | TouchEvent): void => {
217
236
  if (readonly$.value) {
218
237
  return;
@@ -228,11 +247,14 @@ export class TeleBoxManager {
228
247
  el;
229
248
  el = el.parentElement
230
249
  ) {
250
+ if (el.classList && el.classList.contains(closeBtnClassName)) {
251
+ return;
252
+ }
231
253
  const id = el.dataset?.teleBoxID;
232
254
  if (id) {
233
- const box = this.boxes.find((box) => box.id === id);
255
+ const box = this.getBox(id);
234
256
  if (box) {
235
- this.focusBox({ focus: true, box });
257
+ this.focusBox(box);
236
258
  return;
237
259
  }
238
260
  }
@@ -253,11 +275,11 @@ export class TeleBoxManager {
253
275
  );
254
276
 
255
277
  this.maxTitleBar = new MaxTitleBar({
278
+ darkMode: this.darkMode,
256
279
  readonly: readonly$.value,
257
280
  namespace: this.namespace,
258
281
  state: state$.value,
259
- boxes: this.boxes,
260
- focusedBox: this._focusedBox,
282
+ boxes: this.boxes$.value,
261
283
  containerRect: containerRect$.value,
262
284
  onEvent: (event): void => {
263
285
  switch (event.type) {
@@ -270,12 +292,7 @@ export class TeleBoxManager {
270
292
  break;
271
293
  }
272
294
  case TELE_BOX_EVENT.Close: {
273
- const box =
274
- this._focusedBox ??
275
- this.boxes[this.boxes.length - 1];
276
- if (box) {
277
- this.remove(box.id);
278
- }
295
+ this.removeTopBox();
279
296
  break;
280
297
  }
281
298
  default: {
@@ -287,6 +304,12 @@ export class TeleBoxManager {
287
304
  readonly$.subscribe((readonly) =>
288
305
  this.maxTitleBar.setReadonly(readonly)
289
306
  );
307
+ this._darkMode$.subscribe((darkMode) => {
308
+ this.maxTitleBar.setDarkMode(darkMode);
309
+ });
310
+ this.boxes$.reaction((boxes) => {
311
+ this.maxTitleBar.setBoxes(boxes);
312
+ });
290
313
 
291
314
  const valConfig: ValConfig = {
292
315
  prefersColorScheme: prefersColorScheme$,
@@ -305,6 +328,14 @@ export class TeleBoxManager {
305
328
  this.root.appendChild(this.maxTitleBar.render());
306
329
  }
307
330
 
331
+ public get boxes(): ReadonlyArray<TeleBox> {
332
+ return this.boxes$.value;
333
+ }
334
+
335
+ public get topBox(): TeleBox | undefined {
336
+ return this.topBox$.value;
337
+ }
338
+
308
339
  public readonly events = new EventEmitter() as TeleBoxManagerEvents;
309
340
 
310
341
  protected _sideEffect: SideEffectManager;
@@ -313,8 +344,6 @@ export class TeleBoxManager {
313
344
 
314
345
  public readonly namespace: string;
315
346
 
316
- public zIndex: number;
317
-
318
347
  public _darkMode$: Val<boolean, boolean>;
319
348
 
320
349
  public get darkMode(): boolean {
@@ -349,16 +378,30 @@ export class TeleBoxManager {
349
378
  return this;
350
379
  }
351
380
 
352
- public create(config?: TeleBoxManagerCreateConfig): ReadonlyTeleBox {
353
- const box = new TeleBox(this.wrapCreateConfig(config));
381
+ public create(
382
+ config: TeleBoxManagerCreateConfig = {},
383
+ smartPosition = true
384
+ ): ReadonlyTeleBox {
385
+ const box = new TeleBox({
386
+ zIndex: this.topBox ? this.topBox.zIndex + 1 : 100,
387
+ ...(smartPosition ? this.smartPosition(config) : config),
388
+ darkMode: this.darkMode,
389
+ prefersColorScheme: this.prefersColorScheme,
390
+ maximized: this.maximized,
391
+ minimized: this.minimized,
392
+ fence: this.fence,
393
+ namespace: this.namespace,
394
+ containerRect: this.containerRect,
395
+ readonly: this.readonly,
396
+ });
397
+
354
398
  box.mount(this.root);
355
- this.boxes.push(box);
356
399
 
357
400
  if (box.focus) {
358
- this.focusBox({ focus: true, box, increaseZIndex: false });
401
+ this.focusBox(box);
359
402
  }
360
403
 
361
- this.maxTitleBar.setBoxes(this.boxes);
404
+ this.boxes$.setValue([...this.boxes, box]);
362
405
 
363
406
  box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Maximize, () => {
364
407
  this.setMaximized(!this.maximized);
@@ -367,8 +410,7 @@ export class TeleBoxManager {
367
410
  this.setMinimized(true);
368
411
  });
369
412
  box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Close, () => {
370
- this.focusBox({ focus: false, box });
371
- this.remove(box.id);
413
+ this.remove(box);
372
414
  });
373
415
  box.events.on(TELE_BOX_EVENT.Move, () => {
374
416
  this.events.emit(TELE_BOX_MANAGER_EVENT.Move, box);
@@ -425,16 +467,16 @@ export class TeleBoxManager {
425
467
  }
426
468
 
427
469
  public remove(
428
- boxID: string,
470
+ boxOrID: string | TeleBox,
429
471
  skipUpdate = false
430
472
  ): ReadonlyTeleBox | undefined {
431
- const index = this.boxes.findIndex((box) => box.id === boxID);
473
+ const index = this.getBoxIndex(boxOrID);
432
474
  if (index >= 0) {
433
- const boxes = this.boxes.splice(index, 1);
434
- this.maxTitleBar.setBoxes(this.boxes);
435
- const box = boxes[0];
436
- this.focusBox({ focus: false, box });
437
- box.destroy();
475
+ const boxes = this.boxes.slice();
476
+ const deletedBox = boxes.splice(index, 1)[0];
477
+ this.boxes$.setValue(boxes);
478
+ this.focusTopBox();
479
+ deletedBox.destroy();
438
480
  if (!skipUpdate) {
439
481
  if (this.boxes.length <= 0) {
440
482
  this.setMaximized(false);
@@ -442,40 +484,35 @@ export class TeleBoxManager {
442
484
  }
443
485
  this.events.emit(TELE_BOX_MANAGER_EVENT.Removed, boxes);
444
486
  }
445
- return box;
487
+ return deletedBox;
446
488
  }
447
489
  return;
448
490
  }
449
491
 
450
- public removeAll(skipUpdate = false): ReadonlyTeleBox[] {
451
- if (this._focusedBox) {
452
- const box = this._focusedBox;
453
- this._focusedBox = void 0;
454
- if (!skipUpdate) {
455
- this.events.emit(
456
- TELE_BOX_MANAGER_EVENT.Focused,
457
- undefined,
458
- box
459
- );
460
- }
492
+ public removeTopBox(): ReadonlyTeleBox | undefined {
493
+ if (this.topBox) {
494
+ return this.remove(this.topBox);
461
495
  }
462
- const boxes = this.boxes.splice(0, this.boxes.length);
463
- this.maxTitleBar.setBoxes(this.boxes);
464
- boxes.forEach((box) => box.destroy());
496
+ return;
497
+ }
498
+
499
+ public removeAll(skipUpdate = false): ReadonlyTeleBox[] {
500
+ const deletedBoxes = this.boxes$.value;
501
+ this.boxes$.setValue([]);
502
+ deletedBoxes.forEach((box) => box.destroy());
465
503
  if (!skipUpdate) {
466
504
  if (this.boxes.length <= 0) {
467
505
  this.setMaximized(false);
468
506
  this.setMinimized(false);
469
507
  }
470
- this.events.emit(TELE_BOX_MANAGER_EVENT.Removed, boxes);
508
+ this.events.emit(TELE_BOX_MANAGER_EVENT.Removed, deletedBoxes);
471
509
  }
472
- return boxes;
510
+ return deletedBoxes;
473
511
  }
474
512
 
475
513
  public destroy(skipUpdate = false): void {
476
514
  this.events.removeAllListeners();
477
515
  this._sideEffect.flushAll();
478
- this._focusedBox = void 0;
479
516
  this.removeAll(skipUpdate);
480
517
 
481
518
  Object.keys(this).forEach((key) => {
@@ -490,13 +527,70 @@ export class TeleBoxManager {
490
527
  return `${this.namespace}-${className}`;
491
528
  }
492
529
 
493
- protected maxTitleBar: MaxTitleBar;
530
+ public focusBox(boxOrID: string | TeleBox, skipUpdate = false): void {
531
+ const targetBox = this.getBox(boxOrID);
532
+ if (targetBox) {
533
+ this.boxes.forEach((box) => {
534
+ if (targetBox === box) {
535
+ let focusChanged = false;
536
+ if (!targetBox.focus) {
537
+ focusChanged = true;
538
+ targetBox.setFocus(true, skipUpdate);
539
+ }
540
+ this.makeBoxTop(targetBox, skipUpdate);
541
+ if (focusChanged && !skipUpdate) {
542
+ this.events.emit(
543
+ TELE_BOX_MANAGER_EVENT.Focused,
544
+ targetBox
545
+ );
546
+ }
547
+ } else if (box.focus) {
548
+ this.blurBox(box, skipUpdate);
549
+ }
550
+ });
551
+ this.maxTitleBar.focusBox(targetBox);
552
+ }
553
+ }
494
554
 
495
- protected _focusedBox: TeleBox | undefined;
555
+ public focusTopBox(): void {
556
+ if (this.topBox && !this.topBox.focus) {
557
+ return this.focusBox(this.topBox);
558
+ }
559
+ }
496
560
 
497
- protected boxes: TeleBox[] = [];
561
+ public blurBox(boxOrID: string | TeleBox, skipUpdate = false): void {
562
+ const targetBox = this.getBox(boxOrID);
563
+ if (targetBox) {
564
+ if (targetBox.focus) {
565
+ targetBox.setFocus(false, skipUpdate);
566
+ if (!skipUpdate) {
567
+ this.events.emit(TELE_BOX_MANAGER_EVENT.Blurred, targetBox);
568
+ }
569
+ }
570
+ if (this.maxTitleBar.focusedBox === targetBox) {
571
+ this.maxTitleBar.focusBox();
572
+ }
573
+ }
574
+ }
498
575
 
499
- protected lastState: TeleBoxState | undefined;
576
+ public blurAll(skipUpdate = false): void {
577
+ this.boxes.forEach((box) => {
578
+ if (box.focus) {
579
+ box.setFocus(false, skipUpdate);
580
+ if (!skipUpdate) {
581
+ this.events.emit(TELE_BOX_MANAGER_EVENT.Blurred, box);
582
+ }
583
+ }
584
+ });
585
+ if (this.maxTitleBar.focusedBox) {
586
+ this.maxTitleBar.focusBox();
587
+ }
588
+ }
589
+
590
+ protected maxTitleBar: MaxTitleBar;
591
+
592
+ protected boxes$: Val<TeleBox[]>;
593
+ protected topBox$: Val<TeleBox | undefined>;
500
594
 
501
595
  protected teleBoxMatcher(
502
596
  config: TeleBoxManagerQueryConfig
@@ -549,9 +643,6 @@ export class TeleBoxManager {
549
643
  if (config.fixRatio != null) {
550
644
  box.setFixRatio(config.fixRatio, skipUpdate);
551
645
  }
552
- if (config.focus != null) {
553
- this.focusBox({ focus: config.focus, box, skipUpdate });
554
- }
555
646
  if (config.content != null) {
556
647
  box.mountContent(config.content);
557
648
  }
@@ -560,119 +651,67 @@ export class TeleBoxManager {
560
651
  }
561
652
  }
562
653
 
563
- protected focusBox({
564
- focus,
565
- box,
566
- skipUpdate = false,
567
- increaseZIndex = true,
568
- }: {
569
- focus: boolean;
570
- box: TeleBox;
571
- skipUpdate?: boolean;
572
- increaseZIndex?: boolean;
573
- }): void {
574
- box.setFocus(focus, skipUpdate);
575
- if (box.focus) {
576
- if (this._focusedBox !== box) {
577
- const lastFocusedBox = this._focusedBox;
578
- if (this._focusedBox) {
579
- this._focusedBox.setFocus(false, skipUpdate);
580
- }
581
- this._focusedBox = box;
582
- if (increaseZIndex) {
583
- box.setZIndex(++this.zIndex);
584
- }
585
- if (!skipUpdate) {
586
- this.events.emit(
587
- TELE_BOX_MANAGER_EVENT.Focused,
588
- box,
589
- lastFocusedBox
590
- );
591
- }
592
- }
593
- } else {
594
- if (this._focusedBox === box) {
595
- this._focusedBox = void 0;
654
+ protected smartPosition(config: TeleBoxConfig = {}): TeleBoxConfig {
655
+ let { x, y } = config;
656
+ const { width = 0.5, height = 0.5 } = config;
596
657
 
597
- if (!skipUpdate) {
598
- this.events.emit(
599
- TELE_BOX_MANAGER_EVENT.Focused,
600
- undefined,
601
- box
602
- );
658
+ if (x == null) {
659
+ let vx = 20;
660
+ if (this.topBox) {
661
+ vx = this.topBox.intrinsicX * this.containerRect.width + 20;
662
+
663
+ if (
664
+ vx >
665
+ this.containerRect.width - width * this.containerRect.width
666
+ ) {
667
+ vx = 20;
603
668
  }
604
669
  }
670
+ x = vx / this.containerRect.width;
605
671
  }
606
- this.maxTitleBar.focusBox(this._focusedBox);
607
- }
608
-
609
- protected getInitialPosition(
610
- width: number,
611
- height: number
612
- ): { x: number; y: number } {
613
- const upMostBox =
614
- this.boxes.length > 0 &&
615
- this.boxes.reduce((box1, box2) =>
616
- box1.zIndex > box2.zIndex ? box1 : box2
617
- );
618
672
 
619
- let x = 20;
620
- let y = 20;
673
+ if (y == null) {
674
+ let vy = 20;
621
675
 
622
- if (upMostBox) {
623
- x = upMostBox.intrinsicX * this.containerRect.width + 20;
624
- y = upMostBox.intrinsicY * this.containerRect.height + 20;
676
+ if (this.topBox) {
677
+ vy = this.topBox.intrinsicY * this.containerRect.height + 20;
625
678
 
626
- if (
627
- x >
628
- this.containerRect.width -
629
- width * this.containerRect.width ||
630
- y >
679
+ if (
680
+ vy >
631
681
  this.containerRect.height -
632
682
  height * this.containerRect.height
633
- ) {
634
- x = 20;
635
- y = 20;
683
+ ) {
684
+ vy = 20;
685
+ }
636
686
  }
687
+
688
+ y = vy / this.containerRect.height;
637
689
  }
638
690
 
639
- return {
640
- x: x / this.containerRect.width,
641
- y: y / this.containerRect.height,
642
- };
691
+ return { ...config, x, y, width, height };
643
692
  }
644
693
 
645
- protected wrapCreateConfig(config: TeleBoxConfig = {}): TeleBoxConfig {
646
- let { x, y } = config;
647
- const { width = 0.5, height = 0.5 } = config;
648
-
649
- if (x == null || y == null) {
650
- const initialPos = this.getInitialPosition(width, height);
651
-
652
- if (x == null) {
653
- x = initialPos.x;
654
- }
655
-
656
- if (y == null) {
657
- y = initialPos.y;
694
+ protected makeBoxTop(box: TeleBox, skipUpdate = false): void {
695
+ if (this.topBox) {
696
+ if (box !== this.topBox) {
697
+ box.setZIndex(this.topBox.zIndex + 1, skipUpdate);
698
+ this.topBox$.setValue(box);
699
+ if (!skipUpdate) {
700
+ this.events.emit(TELE_BOX_MANAGER_EVENT.ZIndex, box);
701
+ }
658
702
  }
659
703
  }
704
+ }
660
705
 
661
- return {
662
- ...config,
663
- x,
664
- y,
665
- width,
666
- height,
667
- darkMode: this.darkMode,
668
- prefersColorScheme: this.prefersColorScheme,
669
- maximized: this.maximized,
670
- minimized: this.minimized,
671
- fence: this.fence,
672
- zIndex: this.zIndex,
673
- namespace: this.namespace,
674
- containerRect: this.containerRect,
675
- readonly: this.readonly,
676
- };
706
+ protected getBoxIndex(boxOrID: TeleBox | string): number {
707
+ return typeof boxOrID === "string"
708
+ ? this.boxes.findIndex((box) => box.id === boxOrID)
709
+ : this.boxes.findIndex((box) => box === boxOrID);
710
+ }
711
+
712
+ protected getBox(boxOrID: TeleBox | string): TeleBox | undefined {
713
+ return typeof boxOrID === "string"
714
+ ? this.boxes.find((box) => box.id === boxOrID)
715
+ : boxOrID;
677
716
  }
678
717
  }
@@ -14,7 +14,6 @@ export interface TeleBoxManagerConfig
14
14
  | "maximized"
15
15
  | "minimized"
16
16
  | "namespace"
17
- | "zIndex"
18
17
  | "readonly"
19
18
  > {
20
19
  /** Element to mount boxes. */
@@ -35,16 +34,16 @@ type TeleBoxManagerBoxConfigBaseProps =
35
34
  | "resizable"
36
35
  | "draggable"
37
36
  | "fixRatio"
38
- | "focus";
37
+ | "zIndex";
39
38
 
40
39
  export type TeleBoxManagerCreateConfig = Pick<
41
40
  TeleBoxConfig,
42
- TeleBoxManagerBoxConfigBaseProps | "content" | "footer" | "id"
41
+ TeleBoxManagerBoxConfigBaseProps | "content" | "footer" | "id" | "focus"
43
42
  >;
44
43
 
45
44
  export type TeleBoxManagerQueryConfig = Pick<
46
45
  TeleBoxConfig,
47
- TeleBoxManagerBoxConfigBaseProps | "id"
46
+ TeleBoxManagerBoxConfigBaseProps | "id" | "focus"
48
47
  >;
49
48
 
50
49
  export type TeleBoxManagerUpdateConfig = Pick<
@@ -57,8 +56,8 @@ type CheckTeleBoxManagerConfig<
57
56
  > = T;
58
57
 
59
58
  export type TeleBoxManagerEventConfig = CheckTeleBoxManagerConfig<{
60
- /** current focused box and last focused box */
61
- focused: [ReadonlyTeleBox | undefined, ReadonlyTeleBox | undefined];
59
+ focused: [ReadonlyTeleBox | undefined];
60
+ blurred: [ReadonlyTeleBox | undefined];
62
61
  created: [ReadonlyTeleBox];
63
62
  removed: [ReadonlyTeleBox[]];
64
63
  state: [TeleBoxState];
@@ -69,6 +68,7 @@ export type TeleBoxManagerEventConfig = CheckTeleBoxManagerConfig<{
69
68
  intrinsic_move: [ReadonlyTeleBox];
70
69
  intrinsic_resize: [ReadonlyTeleBox];
71
70
  visual_resize: [ReadonlyTeleBox];
71
+ z_index: [ReadonlyTeleBox];
72
72
  prefers_color_scheme: [TeleBoxColorScheme];
73
73
  dark_mode: [boolean];
74
74
  }>;
@@ -73,6 +73,8 @@
73
73
  }
74
74
 
75
75
  .#{$namespace}-color-scheme-dark {
76
+ color-scheme: dark;
77
+
76
78
  .#{$namespace}-title {
77
79
  color: #e9e9e9;
78
80
  }