@netless/telebox-insider 0.2.10 → 0.2.11

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/telebox-insider",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "private": false,
5
5
  "description": "Window widget for Whiteboard",
6
6
  "license": "MIT",
@@ -21,6 +21,7 @@ export enum TELE_BOX_EVENT {
21
21
  IntrinsicMove = "intrinsic_move",
22
22
  IntrinsicResize = "intrinsic_resize",
23
23
  VisualResize = "visual_resize",
24
+ ZIndex = "z_index",
24
25
  State = "state",
25
26
  Minimized = "minimized",
26
27
  Maximized = "maximized",
@@ -197,7 +197,13 @@ export class TeleBox {
197
197
  const draggable$ = createVal(draggable);
198
198
  const fence$ = createVal(fence);
199
199
  const fixRatio$ = createVal(fixRatio);
200
+
200
201
  const zIndex$ = createVal(zIndex);
202
+ zIndex$.reaction((zIndex, _, skipUpdate) => {
203
+ if (!skipUpdate) {
204
+ this.events.emit(TELE_BOX_EVENT.ZIndex, zIndex);
205
+ }
206
+ });
201
207
 
202
208
  const focus$ = createVal(focus);
203
209
  focus$.reaction((focus, _, skipUpdate) => {
@@ -96,6 +96,7 @@ export type TeleBoxEventConfig = CheckTeleBoxConfig<{
96
96
  intrinsic_move: { x: number; y: number };
97
97
  intrinsic_resize: { width: number; height: number };
98
98
  visual_resize: { width: number; height: number };
99
+ z_index: number;
99
100
  state: TeleBoxState;
100
101
  minimized: boolean;
101
102
  maximized: boolean;
@@ -25,10 +25,6 @@ export class MaxTitleBar extends DefaultTitleBar {
25
25
  }
26
26
 
27
27
  public focusBox(box?: MaxTitleBarTeleBox): void {
28
- if (!box) {
29
- box = this.boxes[this.boxes.length - 1];
30
- }
31
-
32
28
  if (this.focusedBox && this.focusedBox === box) {
33
29
  return;
34
30
  }
@@ -200,7 +196,7 @@ export class MaxTitleBar extends DefaultTitleBar {
200
196
 
201
197
  protected boxes: MaxTitleBarTeleBox[];
202
198
 
203
- protected focusedBox: MaxTitleBarTeleBox | undefined;
199
+ public focusedBox: MaxTitleBarTeleBox | undefined;
204
200
 
205
201
  protected containerRect: TeleBoxRect;
206
202
  }
@@ -1,5 +1,6 @@
1
1
  export enum TELE_BOX_MANAGER_EVENT {
2
2
  Focused = "focused",
3
+ Blurred = "blurred",
3
4
  Created = "created",
4
5
  Removed = "removed",
5
6
  State = "state",
@@ -10,6 +11,7 @@ export enum TELE_BOX_MANAGER_EVENT {
10
11
  IntrinsicMove = "intrinsic_move",
11
12
  IntrinsicResize = "intrinsic_resize",
12
13
  VisualResize = "visual_resize",
14
+ ZIndex = "z_index",
13
15
  PrefersColorScheme = "prefers_color_scheme",
14
16
  DarkMode = "dark_mode",
15
17
  }
@@ -69,6 +69,17 @@ export class TeleBoxManager {
69
69
  this.namespace = namespace;
70
70
  this.zIndex = zIndex;
71
71
 
72
+ this.boxes$ = createVal<TeleBox[]>([]);
73
+ this.topBox$ = this.boxes$.derive((boxes) => {
74
+ if (boxes.length > 0) {
75
+ const topBox = boxes.reduce((topBox, box) =>
76
+ topBox.zIndex > box.zIndex ? topBox : box
77
+ );
78
+ return topBox;
79
+ }
80
+ return;
81
+ });
82
+
72
83
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
73
84
  const prefersDark$ = createVal(false);
74
85
 
@@ -221,6 +232,8 @@ export class TeleBoxManager {
221
232
  }
222
233
  });
223
234
 
235
+ const closeBtnClassName = this.wrapClassName("titlebar-icon-close");
236
+
224
237
  const checkFocusBox = (ev: MouseEvent | TouchEvent): void => {
225
238
  if (readonly$.value) {
226
239
  return;
@@ -236,11 +249,14 @@ export class TeleBoxManager {
236
249
  el;
237
250
  el = el.parentElement
238
251
  ) {
252
+ if (el.classList && el.classList.contains(closeBtnClassName)) {
253
+ return;
254
+ }
239
255
  const id = el.dataset?.teleBoxID;
240
256
  if (id) {
241
- const box = this.boxes.find((box) => box.id === id);
257
+ const box = this.getBox(id);
242
258
  if (box) {
243
- this.focusBox({ focus: true, box });
259
+ this.focusBox(box);
244
260
  return;
245
261
  }
246
262
  }
@@ -265,8 +281,7 @@ export class TeleBoxManager {
265
281
  readonly: readonly$.value,
266
282
  namespace: this.namespace,
267
283
  state: state$.value,
268
- boxes: this.boxes,
269
- focusedBox: this._focusedBox,
284
+ boxes: this.boxes$.value,
270
285
  containerRect: containerRect$.value,
271
286
  onEvent: (event): void => {
272
287
  switch (event.type) {
@@ -279,12 +294,7 @@ export class TeleBoxManager {
279
294
  break;
280
295
  }
281
296
  case TELE_BOX_EVENT.Close: {
282
- const box =
283
- this._focusedBox ??
284
- this.boxes[this.boxes.length - 1];
285
- if (box) {
286
- this.remove(box.id);
287
- }
297
+ this.removeTopBox();
288
298
  break;
289
299
  }
290
300
  default: {
@@ -299,6 +309,9 @@ export class TeleBoxManager {
299
309
  this._darkMode$.subscribe((darkMode) => {
300
310
  this.maxTitleBar.setDarkMode(darkMode);
301
311
  });
312
+ this.boxes$.reaction((boxes) => {
313
+ this.maxTitleBar.setBoxes(boxes);
314
+ });
302
315
 
303
316
  const valConfig: ValConfig = {
304
317
  prefersColorScheme: prefersColorScheme$,
@@ -317,6 +330,14 @@ export class TeleBoxManager {
317
330
  this.root.appendChild(this.maxTitleBar.render());
318
331
  }
319
332
 
333
+ public get boxes(): ReadonlyArray<TeleBox> {
334
+ return this.boxes$.value;
335
+ }
336
+
337
+ public get topBox(): TeleBox | undefined {
338
+ return this.topBox$.value;
339
+ }
340
+
320
341
  public readonly events = new EventEmitter() as TeleBoxManagerEvents;
321
342
 
322
343
  protected _sideEffect: SideEffectManager;
@@ -361,16 +382,30 @@ export class TeleBoxManager {
361
382
  return this;
362
383
  }
363
384
 
364
- public create(config?: TeleBoxManagerCreateConfig): ReadonlyTeleBox {
365
- const box = new TeleBox(this.wrapCreateConfig(config));
385
+ public create(
386
+ config: TeleBoxManagerCreateConfig = {},
387
+ smartPosition = true
388
+ ): ReadonlyTeleBox {
389
+ const box = new TeleBox({
390
+ zIndex: this.zIndex,
391
+ ...(smartPosition ? this.smartPosition(config) : config),
392
+ darkMode: this.darkMode,
393
+ prefersColorScheme: this.prefersColorScheme,
394
+ maximized: this.maximized,
395
+ minimized: this.minimized,
396
+ fence: this.fence,
397
+ namespace: this.namespace,
398
+ containerRect: this.containerRect,
399
+ readonly: this.readonly,
400
+ });
401
+
366
402
  box.mount(this.root);
367
- this.boxes.push(box);
368
403
 
369
404
  if (box.focus) {
370
- this.focusBox({ focus: true, box, increaseZIndex: false });
405
+ this.focusBox(box);
371
406
  }
372
407
 
373
- this.maxTitleBar.setBoxes(this.boxes);
408
+ this.boxes$.setValue([...this.boxes, box]);
374
409
 
375
410
  box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Maximize, () => {
376
411
  this.setMaximized(!this.maximized);
@@ -379,8 +414,7 @@ export class TeleBoxManager {
379
414
  this.setMinimized(true);
380
415
  });
381
416
  box._delegateEvents.on(TELE_BOX_DELEGATE_EVENT.Close, () => {
382
- this.focusBox({ focus: false, box });
383
- this.remove(box.id);
417
+ this.remove(box);
384
418
  });
385
419
  box.events.on(TELE_BOX_EVENT.Move, () => {
386
420
  this.events.emit(TELE_BOX_MANAGER_EVENT.Move, box);
@@ -437,16 +471,16 @@ export class TeleBoxManager {
437
471
  }
438
472
 
439
473
  public remove(
440
- boxID: string,
474
+ boxOrID: string | TeleBox,
441
475
  skipUpdate = false
442
476
  ): ReadonlyTeleBox | undefined {
443
- const index = this.boxes.findIndex((box) => box.id === boxID);
477
+ const index = this.getBoxIndex(boxOrID);
444
478
  if (index >= 0) {
445
- const boxes = this.boxes.splice(index, 1);
446
- this.maxTitleBar.setBoxes(this.boxes);
447
- const box = boxes[0];
448
- this.focusBox({ focus: false, box });
449
- box.destroy();
479
+ const boxes = this.boxes.slice();
480
+ const deletedBox = boxes.splice(index, 1)[0];
481
+ this.boxes$.setValue(boxes);
482
+ this.focusTopBox();
483
+ deletedBox.destroy();
450
484
  if (!skipUpdate) {
451
485
  if (this.boxes.length <= 0) {
452
486
  this.setMaximized(false);
@@ -454,40 +488,35 @@ export class TeleBoxManager {
454
488
  }
455
489
  this.events.emit(TELE_BOX_MANAGER_EVENT.Removed, boxes);
456
490
  }
457
- return box;
491
+ return deletedBox;
458
492
  }
459
493
  return;
460
494
  }
461
495
 
462
- public removeAll(skipUpdate = false): ReadonlyTeleBox[] {
463
- if (this._focusedBox) {
464
- const box = this._focusedBox;
465
- this._focusedBox = void 0;
466
- if (!skipUpdate) {
467
- this.events.emit(
468
- TELE_BOX_MANAGER_EVENT.Focused,
469
- undefined,
470
- box
471
- );
472
- }
496
+ public removeTopBox(): ReadonlyTeleBox | undefined {
497
+ if (this.topBox) {
498
+ return this.remove(this.topBox);
473
499
  }
474
- const boxes = this.boxes.splice(0, this.boxes.length);
475
- this.maxTitleBar.setBoxes(this.boxes);
476
- boxes.forEach((box) => box.destroy());
500
+ return;
501
+ }
502
+
503
+ public removeAll(skipUpdate = false): ReadonlyTeleBox[] {
504
+ const deletedBoxes = this.boxes$.value;
505
+ this.boxes$.setValue([]);
506
+ deletedBoxes.forEach((box) => box.destroy());
477
507
  if (!skipUpdate) {
478
508
  if (this.boxes.length <= 0) {
479
509
  this.setMaximized(false);
480
510
  this.setMinimized(false);
481
511
  }
482
- this.events.emit(TELE_BOX_MANAGER_EVENT.Removed, boxes);
512
+ this.events.emit(TELE_BOX_MANAGER_EVENT.Removed, deletedBoxes);
483
513
  }
484
- return boxes;
514
+ return deletedBoxes;
485
515
  }
486
516
 
487
517
  public destroy(skipUpdate = false): void {
488
518
  this.events.removeAllListeners();
489
519
  this._sideEffect.flushAll();
490
- this._focusedBox = void 0;
491
520
  this.removeAll(skipUpdate);
492
521
 
493
522
  Object.keys(this).forEach((key) => {
@@ -502,13 +531,70 @@ export class TeleBoxManager {
502
531
  return `${this.namespace}-${className}`;
503
532
  }
504
533
 
505
- protected maxTitleBar: MaxTitleBar;
534
+ public focusBox(boxOrID: string | TeleBox, skipUpdate = false): void {
535
+ const targetBox = this.getBox(boxOrID);
536
+ if (targetBox) {
537
+ this.boxes.forEach((box) => {
538
+ if (targetBox === box) {
539
+ let focusChanged = false;
540
+ if (!targetBox.focus) {
541
+ focusChanged = true;
542
+ targetBox.setFocus(true, skipUpdate);
543
+ }
544
+ this.makeBoxTop(targetBox, skipUpdate);
545
+ if (focusChanged && !skipUpdate) {
546
+ this.events.emit(
547
+ TELE_BOX_MANAGER_EVENT.Focused,
548
+ targetBox
549
+ );
550
+ }
551
+ } else if (box.focus) {
552
+ this.blurBox(box, skipUpdate);
553
+ }
554
+ });
555
+ this.maxTitleBar.focusBox(targetBox);
556
+ }
557
+ }
558
+
559
+ public focusTopBox(): void {
560
+ if (this.topBox && !this.topBox.focus) {
561
+ return this.focusBox(this.topBox);
562
+ }
563
+ }
564
+
565
+ public blurBox(boxOrID: string | TeleBox, skipUpdate = false): void {
566
+ const targetBox = this.getBox(boxOrID);
567
+ if (targetBox) {
568
+ if (targetBox.focus) {
569
+ targetBox.setFocus(false, skipUpdate);
570
+ if (!skipUpdate) {
571
+ this.events.emit(TELE_BOX_MANAGER_EVENT.Blurred, targetBox);
572
+ }
573
+ }
574
+ if (this.maxTitleBar.focusedBox === targetBox) {
575
+ this.maxTitleBar.focusBox();
576
+ }
577
+ }
578
+ }
506
579
 
507
- protected _focusedBox: TeleBox | undefined;
580
+ public blurAll(skipUpdate = false): void {
581
+ this.boxes.forEach((box) => {
582
+ if (box.focus) {
583
+ box.setFocus(false, skipUpdate);
584
+ if (!skipUpdate) {
585
+ this.events.emit(TELE_BOX_MANAGER_EVENT.Blurred, box);
586
+ }
587
+ }
588
+ });
589
+ if (this.maxTitleBar.focusedBox) {
590
+ this.maxTitleBar.focusBox();
591
+ }
592
+ }
508
593
 
509
- protected boxes: TeleBox[] = [];
594
+ protected maxTitleBar: MaxTitleBar;
510
595
 
511
- protected lastState: TeleBoxState | undefined;
596
+ protected boxes$: Val<TeleBox[]>;
597
+ protected topBox$: Val<TeleBox | undefined>;
512
598
 
513
599
  protected teleBoxMatcher(
514
600
  config: TeleBoxManagerQueryConfig
@@ -561,9 +647,6 @@ export class TeleBoxManager {
561
647
  if (config.fixRatio != null) {
562
648
  box.setFixRatio(config.fixRatio, skipUpdate);
563
649
  }
564
- if (config.focus != null) {
565
- this.focusBox({ focus: config.focus, box, skipUpdate });
566
- }
567
650
  if (config.content != null) {
568
651
  box.mountContent(config.content);
569
652
  }
@@ -572,119 +655,69 @@ export class TeleBoxManager {
572
655
  }
573
656
  }
574
657
 
575
- protected focusBox({
576
- focus,
577
- box,
578
- skipUpdate = false,
579
- increaseZIndex = true,
580
- }: {
581
- focus: boolean;
582
- box: TeleBox;
583
- skipUpdate?: boolean;
584
- increaseZIndex?: boolean;
585
- }): void {
586
- box.setFocus(focus, skipUpdate);
587
- if (box.focus) {
588
- if (this._focusedBox !== box) {
589
- const lastFocusedBox = this._focusedBox;
590
- if (this._focusedBox) {
591
- this._focusedBox.setFocus(false, skipUpdate);
592
- }
593
- this._focusedBox = box;
594
- if (increaseZIndex) {
595
- box.setZIndex(++this.zIndex);
596
- }
597
- if (!skipUpdate) {
598
- this.events.emit(
599
- TELE_BOX_MANAGER_EVENT.Focused,
600
- box,
601
- lastFocusedBox
602
- );
603
- }
604
- }
605
- } else {
606
- if (this._focusedBox === box) {
607
- this._focusedBox = void 0;
658
+ protected smartPosition(config: TeleBoxConfig = {}): TeleBoxConfig {
659
+ let { x, y } = config;
660
+ const { width = 0.5, height = 0.5 } = config;
608
661
 
609
- if (!skipUpdate) {
610
- this.events.emit(
611
- TELE_BOX_MANAGER_EVENT.Focused,
612
- undefined,
613
- box
614
- );
662
+ if (x == null) {
663
+ let vx = 20;
664
+ if (this.topBox) {
665
+ vx = this.topBox.intrinsicX * this.containerRect.width + 20;
666
+
667
+ if (
668
+ vx >
669
+ this.containerRect.width - width * this.containerRect.width
670
+ ) {
671
+ vx = 20;
615
672
  }
616
673
  }
674
+ x = vx / this.containerRect.width;
617
675
  }
618
- this.maxTitleBar.focusBox(this._focusedBox);
619
- }
620
676
 
621
- protected getInitialPosition(
622
- width: number,
623
- height: number
624
- ): { x: number; y: number } {
625
- const upMostBox =
626
- this.boxes.length > 0 &&
627
- this.boxes.reduce((box1, box2) =>
628
- box1.zIndex > box2.zIndex ? box1 : box2
629
- );
630
-
631
- let x = 20;
632
- let y = 20;
677
+ if (y == null) {
678
+ let vy = 20;
633
679
 
634
- if (upMostBox) {
635
- x = upMostBox.intrinsicX * this.containerRect.width + 20;
636
- y = upMostBox.intrinsicY * this.containerRect.height + 20;
680
+ if (this.topBox) {
681
+ vy = this.topBox.intrinsicY * this.containerRect.height + 20;
637
682
 
638
- if (
639
- x >
640
- this.containerRect.width -
641
- width * this.containerRect.width ||
642
- y >
683
+ if (
684
+ vy >
643
685
  this.containerRect.height -
644
686
  height * this.containerRect.height
645
- ) {
646
- x = 20;
647
- y = 20;
687
+ ) {
688
+ vy = 20;
689
+ }
648
690
  }
691
+
692
+ y = vy / this.containerRect.height;
649
693
  }
650
694
 
651
- return {
652
- x: x / this.containerRect.width,
653
- y: y / this.containerRect.height,
654
- };
695
+ return { ...config, x, y, width, height };
655
696
  }
656
697
 
657
- protected wrapCreateConfig(config: TeleBoxConfig = {}): TeleBoxConfig {
658
- let { x, y } = config;
659
- const { width = 0.5, height = 0.5 } = config;
660
-
661
- if (x == null || y == null) {
662
- const initialPos = this.getInitialPosition(width, height);
663
-
664
- if (x == null) {
665
- x = initialPos.x;
666
- }
667
-
668
- if (y == null) {
669
- y = initialPos.y;
698
+ protected makeBoxTop(box: TeleBox, skipUpdate = false): void {
699
+ if (box !== this.topBox) {
700
+ const maxIndex = this.boxes.reduce(
701
+ (max, box) => Math.max(max, box.zIndex),
702
+ 0
703
+ );
704
+ box.setZIndex(maxIndex + 1, skipUpdate);
705
+ this.topBox$.setValue(box);
706
+ if (!skipUpdate) {
707
+ this.events.emit(TELE_BOX_MANAGER_EVENT.ZIndex, box);
670
708
  }
671
709
  }
710
+ }
672
711
 
673
- return {
674
- ...config,
675
- x,
676
- y,
677
- width,
678
- height,
679
- darkMode: this.darkMode,
680
- prefersColorScheme: this.prefersColorScheme,
681
- maximized: this.maximized,
682
- minimized: this.minimized,
683
- fence: this.fence,
684
- zIndex: this.zIndex,
685
- namespace: this.namespace,
686
- containerRect: this.containerRect,
687
- readonly: this.readonly,
688
- };
712
+ protected getBoxIndex(boxOrID: TeleBox | string): number {
713
+ return typeof boxOrID === "string"
714
+ ? this.boxes.findIndex((box) => box.id === boxOrID)
715
+ : this.boxes.findIndex((box) => box === boxOrID);
716
+ }
717
+
718
+ protected getBox(boxOrID: TeleBox | string): TeleBox | undefined {
719
+ return typeof boxOrID === "string"
720
+ ? this.boxes.find((box) => box.id === boxOrID)
721
+ : boxOrID;
689
722
  }
690
723
  }
@@ -35,16 +35,16 @@ type TeleBoxManagerBoxConfigBaseProps =
35
35
  | "resizable"
36
36
  | "draggable"
37
37
  | "fixRatio"
38
- | "focus";
38
+ | "zIndex";
39
39
 
40
40
  export type TeleBoxManagerCreateConfig = Pick<
41
41
  TeleBoxConfig,
42
- TeleBoxManagerBoxConfigBaseProps | "content" | "footer" | "id"
42
+ TeleBoxManagerBoxConfigBaseProps | "content" | "footer" | "id" | "focus"
43
43
  >;
44
44
 
45
45
  export type TeleBoxManagerQueryConfig = Pick<
46
46
  TeleBoxConfig,
47
- TeleBoxManagerBoxConfigBaseProps | "id"
47
+ TeleBoxManagerBoxConfigBaseProps | "id" | "focus"
48
48
  >;
49
49
 
50
50
  export type TeleBoxManagerUpdateConfig = Pick<
@@ -57,8 +57,8 @@ type CheckTeleBoxManagerConfig<
57
57
  > = T;
58
58
 
59
59
  export type TeleBoxManagerEventConfig = CheckTeleBoxManagerConfig<{
60
- /** current focused box and last focused box */
61
- focused: [ReadonlyTeleBox | undefined, ReadonlyTeleBox | undefined];
60
+ focused: [ReadonlyTeleBox | undefined];
61
+ blurred: [ReadonlyTeleBox | undefined];
62
62
  created: [ReadonlyTeleBox];
63
63
  removed: [ReadonlyTeleBox[]];
64
64
  state: [TeleBoxState];
@@ -69,6 +69,7 @@ export type TeleBoxManagerEventConfig = CheckTeleBoxManagerConfig<{
69
69
  intrinsic_move: [ReadonlyTeleBox];
70
70
  intrinsic_resize: [ReadonlyTeleBox];
71
71
  visual_resize: [ReadonlyTeleBox];
72
+ z_index: [ReadonlyTeleBox];
72
73
  prefers_color_scheme: [TeleBoxColorScheme];
73
74
  dark_mode: [boolean];
74
75
  }>;