@deck.gl/widgets 9.2.0-beta.2 → 9.2.0-beta.4

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 (125) hide show
  1. package/dist/compass-widget.d.ts +1 -1
  2. package/dist/compass-widget.d.ts.map +1 -1
  3. package/dist/compass-widget.js +0 -1
  4. package/dist/compass-widget.js.map +1 -1
  5. package/dist/context-menu-widget.d.ts +2 -2
  6. package/dist/context-menu-widget.d.ts.map +1 -1
  7. package/dist/context-menu-widget.js +4 -1
  8. package/dist/context-menu-widget.js.map +1 -1
  9. package/dist/dist.dev.js +965 -900
  10. package/dist/fps-widget.d.ts +5 -6
  11. package/dist/fps-widget.d.ts.map +1 -1
  12. package/dist/fps-widget.js +14 -15
  13. package/dist/fps-widget.js.map +1 -1
  14. package/dist/fullscreen-widget.d.ts +2 -0
  15. package/dist/fullscreen-widget.d.ts.map +1 -1
  16. package/dist/fullscreen-widget.js +7 -3
  17. package/dist/fullscreen-widget.js.map +1 -1
  18. package/dist/geocoder-widget.d.ts +4 -3
  19. package/dist/geocoder-widget.d.ts.map +1 -1
  20. package/dist/geocoder-widget.js +3 -4
  21. package/dist/geocoder-widget.js.map +1 -1
  22. package/dist/gimbal-widget.d.ts +7 -5
  23. package/dist/gimbal-widget.d.ts.map +1 -1
  24. package/dist/gimbal-widget.js +15 -10
  25. package/dist/gimbal-widget.js.map +1 -1
  26. package/dist/index.cjs +685 -668
  27. package/dist/index.cjs.map +4 -4
  28. package/dist/index.d.ts +7 -7
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +12 -9
  31. package/dist/index.js.map +1 -1
  32. package/dist/info-widget.d.ts +9 -9
  33. package/dist/info-widget.d.ts.map +1 -1
  34. package/dist/info-widget.js +7 -9
  35. package/dist/info-widget.js.map +1 -1
  36. package/dist/lib/components/button-group.d.ts +1 -1
  37. package/dist/lib/components/button-group.d.ts.map +1 -1
  38. package/dist/lib/components/button-group.js +1 -1
  39. package/dist/lib/components/button-group.js.map +1 -1
  40. package/dist/lib/components/grouped-icon-button.d.ts +5 -3
  41. package/dist/lib/components/grouped-icon-button.d.ts.map +1 -1
  42. package/dist/lib/components/grouped-icon-button.js +3 -3
  43. package/dist/lib/components/grouped-icon-button.js.map +1 -1
  44. package/dist/lib/components/icon-button.d.ts +5 -6
  45. package/dist/lib/components/icon-button.d.ts.map +1 -1
  46. package/dist/lib/components/icon-button.js +1 -1
  47. package/dist/lib/components/icon-button.js.map +1 -1
  48. package/dist/lib/components/icon-menu.d.ts +2 -2
  49. package/dist/lib/components/icon-menu.d.ts.map +1 -1
  50. package/dist/lib/components/icon-menu.js +4 -19
  51. package/dist/lib/components/icon-menu.js.map +1 -1
  52. package/dist/lib/components/simple-menu.js +1 -1
  53. package/dist/lib/components/simple-menu.js.map +1 -1
  54. package/dist/loading-widget.d.ts +4 -2
  55. package/dist/loading-widget.d.ts.map +1 -1
  56. package/dist/loading-widget.js +5 -3
  57. package/dist/loading-widget.js.map +1 -1
  58. package/dist/reset-view-widget.d.ts +9 -11
  59. package/dist/reset-view-widget.d.ts.map +1 -1
  60. package/dist/reset-view-widget.js +5 -4
  61. package/dist/reset-view-widget.js.map +1 -1
  62. package/dist/scale-widget.d.ts +6 -3
  63. package/dist/scale-widget.d.ts.map +1 -1
  64. package/dist/scale-widget.js +3 -2
  65. package/dist/scale-widget.js.map +1 -1
  66. package/dist/screenshot-widget.d.ts +4 -2
  67. package/dist/screenshot-widget.d.ts.map +1 -1
  68. package/dist/screenshot-widget.js +3 -1
  69. package/dist/screenshot-widget.js.map +1 -1
  70. package/dist/splitter-widget.d.ts +1 -1
  71. package/dist/splitter-widget.d.ts.map +1 -1
  72. package/dist/splitter-widget.js +0 -2
  73. package/dist/splitter-widget.js.map +1 -1
  74. package/dist/stats-widget.d.ts +6 -1
  75. package/dist/stats-widget.d.ts.map +1 -1
  76. package/dist/stats-widget.js +11 -4
  77. package/dist/stats-widget.js.map +1 -1
  78. package/dist/stylesheet.css +53 -35
  79. package/dist/theme-widget.d.ts +3 -1
  80. package/dist/theme-widget.d.ts.map +1 -1
  81. package/dist/theme-widget.js +6 -3
  82. package/dist/theme-widget.js.map +1 -1
  83. package/dist/themes.d.ts +10 -0
  84. package/dist/themes.d.ts.map +1 -1
  85. package/dist/themes.js +12 -4
  86. package/dist/themes.js.map +1 -1
  87. package/dist/timeline-widget.d.ts +10 -32
  88. package/dist/timeline-widget.d.ts.map +1 -1
  89. package/dist/timeline-widget.js +6 -2
  90. package/dist/timeline-widget.js.map +1 -1
  91. package/dist/view-selector-widget.d.ts +2 -0
  92. package/dist/view-selector-widget.d.ts.map +1 -1
  93. package/dist/view-selector-widget.js +14 -13
  94. package/dist/view-selector-widget.js.map +1 -1
  95. package/dist/zoom-widget.d.ts +1 -2
  96. package/dist/zoom-widget.d.ts.map +1 -1
  97. package/dist/zoom-widget.js +7 -5
  98. package/dist/zoom-widget.js.map +1 -1
  99. package/dist.min.js +2 -3
  100. package/package.json +2 -2
  101. package/src/compass-widget.tsx +1 -1
  102. package/src/context-menu-widget.tsx +7 -3
  103. package/src/fps-widget.tsx +26 -17
  104. package/src/fullscreen-widget.tsx +9 -3
  105. package/src/geocoder-widget.tsx +7 -7
  106. package/src/gimbal-widget.tsx +16 -11
  107. package/src/index.ts +15 -10
  108. package/src/info-widget.tsx +17 -17
  109. package/src/lib/components/button-group.tsx +2 -2
  110. package/src/lib/components/grouped-icon-button.tsx +9 -6
  111. package/src/lib/components/icon-button.tsx +5 -6
  112. package/src/lib/components/icon-menu.tsx +12 -30
  113. package/src/lib/components/simple-menu.tsx +1 -1
  114. package/src/loading-widget.tsx +9 -5
  115. package/src/reset-view-widget.tsx +18 -16
  116. package/src/scale-widget.tsx +9 -5
  117. package/src/screenshot-widget.tsx +7 -3
  118. package/src/splitter-widget.tsx +1 -3
  119. package/src/stats-widget.tsx +18 -5
  120. package/src/stylesheet.css +53 -35
  121. package/src/theme-widget.tsx +9 -4
  122. package/src/themes.ts +15 -4
  123. package/src/timeline-widget.tsx +19 -42
  124. package/src/view-selector-widget.tsx +58 -15
  125. package/src/zoom-widget.tsx +8 -6
package/dist/dist.dev.js CHANGED
@@ -438,21 +438,21 @@ var __exports__ = (() => {
438
438
 
439
439
  // src/lib/components/button-group.tsx
440
440
  var ButtonGroup = (props) => {
441
- const { children, orientation } = props;
441
+ const { children, orientation = "horizontal" } = props;
442
442
  return /* @__PURE__ */ u2("div", { className: `deck-widget-button-group ${orientation}`, children });
443
443
  };
444
444
 
445
445
  // src/lib/components/grouped-icon-button.tsx
446
446
  var GroupedIconButton = (props) => {
447
- const { className, label, onClick } = props;
447
+ const { className = "", label, onClick, children } = props;
448
448
  return /* @__PURE__ */ u2(
449
449
  "button",
450
450
  {
451
- className: `deck-widget-icon-button ${className || ""}`,
451
+ className: `deck-widget-icon-button ${className}`,
452
452
  type: "button",
453
453
  onClick,
454
454
  title: label,
455
- children: /* @__PURE__ */ u2("div", { className: "deck-widget-icon" })
455
+ children: children ? children : /* @__PURE__ */ u2("div", { className: "deck-widget-icon" })
456
456
  }
457
457
  );
458
458
  };
@@ -463,10 +463,8 @@ var __exports__ = (() => {
463
463
  super(props, _ZoomWidget.defaultProps);
464
464
  this.className = "deck-widget-zoom";
465
465
  this.placement = "top-left";
466
- this.viewId = null;
467
466
  this.viewports = {};
468
- this.viewId = props.viewId ?? this.viewId;
469
- this.placement = props.placement ?? this.placement;
467
+ this.setProps(this.props);
470
468
  }
471
469
  setProps(props) {
472
470
  this.placement = props.placement ?? this.placement;
@@ -505,7 +503,9 @@ var __exports__ = (() => {
505
503
  };
506
504
  if (this.props.transitionDuration > 0) {
507
505
  nextViewState.transitionDuration = this.props.transitionDuration;
508
- nextViewState.transitionInterpolator = "latitude" in nextViewState ? new import_core2.FlyToInterpolator() : new import_core2.LinearInterpolator();
506
+ nextViewState.transitionInterpolator = "latitude" in nextViewState ? new import_core2.FlyToInterpolator() : new import_core2.LinearInterpolator({
507
+ transitionProps: ["zoom"]
508
+ });
509
509
  }
510
510
  this.setViewState(viewId, nextViewState);
511
511
  }
@@ -533,7 +533,7 @@ var __exports__ = (() => {
533
533
  transitionDuration: 200,
534
534
  zoomInLabel: "Zoom In",
535
535
  zoomOutLabel: "Zoom Out",
536
- viewId: void 0
536
+ viewId: null
537
537
  };
538
538
 
539
539
  // src/reset-view-widget.tsx
@@ -541,7 +541,7 @@ var __exports__ = (() => {
541
541
 
542
542
  // src/lib/components/icon-button.tsx
543
543
  var IconButton = (props) => {
544
- const { className, label, onClick, children } = props;
544
+ const { className = "", label, onClick, children } = props;
545
545
  return /* @__PURE__ */ u2("div", { className: "deck-widget-button", children: /* @__PURE__ */ u2(
546
546
  "button",
547
547
  {
@@ -560,10 +560,11 @@ var __exports__ = (() => {
560
560
  super(props, _ResetViewWidget.defaultProps);
561
561
  this.className = "deck-widget-reset-view";
562
562
  this.placement = "top-left";
563
- this.placement = props.placement ?? this.placement;
563
+ this.setProps(this.props);
564
564
  }
565
565
  setProps(props) {
566
566
  this.placement = props.placement ?? this.placement;
567
+ this.viewId = props.viewId ?? this.viewId;
567
568
  super.setProps(props);
568
569
  }
569
570
  onRenderHTML(rootElement) {
@@ -584,9 +585,9 @@ var __exports__ = (() => {
584
585
  this.setViewState(initialViewState);
585
586
  }
586
587
  setViewState(viewState) {
587
- const viewId = this.props.viewId || viewState?.id || "default-view";
588
+ const viewId = this.props.viewId || "default-view";
588
589
  const nextViewState = {
589
- ...viewState
590
+ ...viewId !== "default-view" ? viewState?.[viewId] : viewState
590
591
  // only works for geospatial?
591
592
  // transitionDuration: this.props.transitionDuration,
592
593
  // transitionInterpolator: new FlyToInterpolator()
@@ -601,17 +602,16 @@ var __exports__ = (() => {
601
602
  placement: "top-left",
602
603
  label: "Reset View",
603
604
  initialViewState: void 0,
604
- viewId: void 0
605
+ viewId: null
605
606
  };
606
607
 
607
- // src/compass-widget.tsx
608
+ // src/gimbal-widget.tsx
608
609
  var import_core4 = __toESM(require_core(), 1);
609
- var _CompassWidget = class extends import_core4.Widget {
610
+ var _GimbalWidget = class extends import_core4.Widget {
610
611
  constructor(props = {}) {
611
- super(props, _CompassWidget.defaultProps);
612
- this.className = "deck-widget-compass";
612
+ super(props, _GimbalWidget.defaultProps);
613
+ this.className = "deck-widget-gimbal";
613
614
  this.placement = "top-left";
614
- this.viewId = null;
615
615
  this.viewports = {};
616
616
  this.setProps(this.props);
617
617
  }
@@ -623,101 +623,17 @@ var __exports__ = (() => {
623
623
  onRenderHTML(rootElement) {
624
624
  const viewId = this.viewId || Object.values(this.viewports)[0]?.id || "default-view";
625
625
  const widgetViewport = this.viewports[viewId];
626
- const [rz, rx] = this.getRotation(widgetViewport);
627
- const ui = /* @__PURE__ */ u2("div", { className: "deck-widget-button", style: { perspective: 100 }, children: /* @__PURE__ */ u2(
626
+ const { rotationOrbit, rotationX } = this.getNormalizedRotation(widgetViewport);
627
+ const ui = /* @__PURE__ */ u2("div", { className: "deck-widget-button", style: { perspective: 100, pointerEvents: "auto" }, children: /* @__PURE__ */ u2(
628
628
  "button",
629
629
  {
630
630
  type: "button",
631
631
  onClick: () => {
632
632
  for (const viewport of Object.values(this.viewports)) {
633
- this.handleCompassReset(viewport);
633
+ this.resetOrbitView(viewport);
634
634
  }
635
635
  },
636
636
  title: this.props.label,
637
- style: { transform: `rotateX(${rx}deg)` },
638
- children: /* @__PURE__ */ u2("svg", { fill: "none", width: "100%", height: "100%", viewBox: "0 0 26 26", children: /* @__PURE__ */ u2("g", { transform: `rotate(${rz},13,13)`, children: [
639
- /* @__PURE__ */ u2(
640
- "path",
641
- {
642
- d: "M10 13.0001L12.9999 5L15.9997 13.0001H10Z",
643
- fill: "var(--icon-compass-north-color, rgb(240, 92, 68))"
644
- }
645
- ),
646
- /* @__PURE__ */ u2(
647
- "path",
648
- {
649
- d: "M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z",
650
- fill: "var(--icon-compass-south-color, rgb(204, 204, 204))"
651
- }
652
- )
653
- ] }) })
654
- }
655
- ) });
656
- B(ui, rootElement);
657
- }
658
- onViewportChange(viewport) {
659
- if (!viewport.equals(this.viewports[viewport.id])) {
660
- this.viewports[viewport.id] = viewport;
661
- this.updateHTML();
662
- }
663
- }
664
- getRotation(viewport) {
665
- if (viewport instanceof import_core4.WebMercatorViewport) {
666
- return [-viewport.bearing, viewport.pitch];
667
- } else if (viewport instanceof import_core4._GlobeViewport) {
668
- return [0, Math.max(-80, Math.min(80, viewport.latitude))];
669
- }
670
- return [0, 0];
671
- }
672
- handleCompassReset(viewport) {
673
- const viewId = this.viewId || viewport.id || "default-view";
674
- if (viewport instanceof import_core4.WebMercatorViewport) {
675
- const nextViewState = {
676
- ...viewport,
677
- bearing: 0,
678
- ...this.getRotation(viewport)[0] === 0 ? { pitch: 0 } : {},
679
- transitionDuration: this.props.transitionDuration,
680
- transitionInterpolator: new import_core4.FlyToInterpolator()
681
- };
682
- this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
683
- }
684
- }
685
- };
686
- var CompassWidget = _CompassWidget;
687
- CompassWidget.defaultProps = {
688
- ...import_core4.Widget.defaultProps,
689
- id: "compass",
690
- placement: "top-left",
691
- viewId: null,
692
- label: "Reset Compass",
693
- transitionDuration: 200
694
- };
695
-
696
- // src/gimbal-widget.tsx
697
- var import_core5 = __toESM(require_core(), 1);
698
- var _GimbalWidget = class extends import_core5.Widget {
699
- constructor(props = {}) {
700
- super(props, _GimbalWidget.defaultProps);
701
- this.className = "deck-widget-gimbal";
702
- this.placement = "top-left";
703
- this.viewId = null;
704
- this.setProps(this.props);
705
- }
706
- setProps(props) {
707
- this.placement = props.placement ?? this.placement;
708
- this.viewId = props.viewId ?? this.viewId;
709
- super.setProps(props);
710
- }
711
- onRenderHTML(rootElement) {
712
- const { rotationOrbit, rotationX } = this.getNormalizedRotation();
713
- const ui = /* @__PURE__ */ u2("div", { className: "deck-widget-button", style: { perspective: 100, pointerEvents: "auto" }, children: /* @__PURE__ */ u2(
714
- "button",
715
- {
716
- type: "button",
717
- onClick: () => {
718
- this.resetOrbitView();
719
- },
720
- title: this.props.label,
721
637
  style: { position: "relative", width: 26, height: 26 },
722
638
  children: [
723
639
  /* @__PURE__ */ u2(
@@ -778,10 +694,11 @@ var __exports__ = (() => {
778
694
  B(ui, rootElement);
779
695
  }
780
696
  onViewportChange(viewport) {
697
+ this.viewports[viewport.id] = viewport;
781
698
  this.updateHTML();
782
699
  }
783
- resetOrbitView() {
784
- const viewId = this.getViewId();
700
+ resetOrbitView(viewport) {
701
+ const viewId = this.getViewId(viewport);
785
702
  const viewState = this.getViewState(viewId);
786
703
  if ("rotationOrbit" in viewState || "rotationX" in viewState) {
787
704
  const nextViewState = {
@@ -789,15 +706,15 @@ var __exports__ = (() => {
789
706
  rotationOrbit: 0,
790
707
  rotationX: 0,
791
708
  transitionDuration: this.props.transitionDuration,
792
- transitionInterpolator: new import_core5.LinearInterpolator({
709
+ transitionInterpolator: new import_core4.LinearInterpolator({
793
710
  transitionProps: ["rotationOrbit", "rotationX"]
794
711
  })
795
712
  };
796
713
  this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
797
714
  }
798
715
  }
799
- getNormalizedRotation() {
800
- const viewState = this.getViewState(this.getViewId());
716
+ getNormalizedRotation(viewport) {
717
+ const viewState = this.getViewState(this.getViewId(viewport));
801
718
  const [rz, rx] = this.getRotation(viewState);
802
719
  const rotationOrbit = normalizeAndClampAngle(rz);
803
720
  const rotationX = normalizeAndClampAngle(rx);
@@ -810,8 +727,8 @@ var __exports__ = (() => {
810
727
  return [0, 0];
811
728
  }
812
729
  // Move to Widget/WidgetManager?
813
- getViewId() {
814
- const viewId = this.viewId || "OrbitView";
730
+ getViewId(viewport) {
731
+ const viewId = this.viewId || viewport?.id || "OrbitView";
815
732
  return viewId;
816
733
  }
817
734
  getViewState(viewId) {
@@ -829,10 +746,10 @@ var __exports__ = (() => {
829
746
  };
830
747
  var GimbalWidget = _GimbalWidget;
831
748
  GimbalWidget.defaultProps = {
832
- ...import_core5.Widget.defaultProps,
749
+ ...import_core4.Widget.defaultProps,
833
750
  id: "gimbal",
834
751
  placement: "top-left",
835
- viewId: void 0,
752
+ viewId: null,
836
753
  label: "Gimbal",
837
754
  strokeWidth: 1.5,
838
755
  transitionDuration: 200
@@ -851,6 +768,94 @@ var __exports__ = (() => {
851
768
  return normalized;
852
769
  }
853
770
 
771
+ // src/compass-widget.tsx
772
+ var import_core5 = __toESM(require_core(), 1);
773
+ var _CompassWidget = class extends import_core5.Widget {
774
+ constructor(props = {}) {
775
+ super(props, _CompassWidget.defaultProps);
776
+ this.className = "deck-widget-compass";
777
+ this.placement = "top-left";
778
+ this.viewports = {};
779
+ this.setProps(this.props);
780
+ }
781
+ setProps(props) {
782
+ this.placement = props.placement ?? this.placement;
783
+ this.viewId = props.viewId ?? this.viewId;
784
+ super.setProps(props);
785
+ }
786
+ onRenderHTML(rootElement) {
787
+ const viewId = this.viewId || Object.values(this.viewports)[0]?.id || "default-view";
788
+ const widgetViewport = this.viewports[viewId];
789
+ const [rz, rx] = this.getRotation(widgetViewport);
790
+ const ui = /* @__PURE__ */ u2("div", { className: "deck-widget-button", style: { perspective: 100 }, children: /* @__PURE__ */ u2(
791
+ "button",
792
+ {
793
+ type: "button",
794
+ onClick: () => {
795
+ for (const viewport of Object.values(this.viewports)) {
796
+ this.handleCompassReset(viewport);
797
+ }
798
+ },
799
+ title: this.props.label,
800
+ style: { transform: `rotateX(${rx}deg)` },
801
+ children: /* @__PURE__ */ u2("svg", { fill: "none", width: "100%", height: "100%", viewBox: "0 0 26 26", children: /* @__PURE__ */ u2("g", { transform: `rotate(${rz},13,13)`, children: [
802
+ /* @__PURE__ */ u2(
803
+ "path",
804
+ {
805
+ d: "M10 13.0001L12.9999 5L15.9997 13.0001H10Z",
806
+ fill: "var(--icon-compass-north-color, rgb(240, 92, 68))"
807
+ }
808
+ ),
809
+ /* @__PURE__ */ u2(
810
+ "path",
811
+ {
812
+ d: "M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z",
813
+ fill: "var(--icon-compass-south-color, rgb(204, 204, 204))"
814
+ }
815
+ )
816
+ ] }) })
817
+ }
818
+ ) });
819
+ B(ui, rootElement);
820
+ }
821
+ onViewportChange(viewport) {
822
+ if (!viewport.equals(this.viewports[viewport.id])) {
823
+ this.viewports[viewport.id] = viewport;
824
+ this.updateHTML();
825
+ }
826
+ }
827
+ getRotation(viewport) {
828
+ if (viewport instanceof import_core5.WebMercatorViewport) {
829
+ return [-viewport.bearing, viewport.pitch];
830
+ } else if (viewport instanceof import_core5._GlobeViewport) {
831
+ return [0, Math.max(-80, Math.min(80, viewport.latitude))];
832
+ }
833
+ return [0, 0];
834
+ }
835
+ handleCompassReset(viewport) {
836
+ const viewId = this.viewId || viewport.id || "default-view";
837
+ if (viewport instanceof import_core5.WebMercatorViewport) {
838
+ const nextViewState = {
839
+ ...viewport,
840
+ bearing: 0,
841
+ ...this.getRotation(viewport)[0] === 0 ? { pitch: 0 } : {},
842
+ transitionDuration: this.props.transitionDuration,
843
+ transitionInterpolator: new import_core5.FlyToInterpolator()
844
+ };
845
+ this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
846
+ }
847
+ }
848
+ };
849
+ var CompassWidget = _CompassWidget;
850
+ CompassWidget.defaultProps = {
851
+ ...import_core5.Widget.defaultProps,
852
+ id: "compass",
853
+ placement: "top-left",
854
+ viewId: null,
855
+ label: "Reset Compass",
856
+ transitionDuration: 200
857
+ };
858
+
854
859
  // src/scale-widget.tsx
855
860
  var import_core6 = __toESM(require_core(), 1);
856
861
  var _ScaleWidget = class extends import_core6.Widget {
@@ -864,10 +869,11 @@ var __exports__ = (() => {
864
869
  this.scaleValue = 0;
865
870
  // The formatted distance label (e.g. "200 m" or "1.0 km")
866
871
  this.scaleText = "";
867
- this.placement = props.placement ?? this.placement;
872
+ this.setProps(this.props);
868
873
  }
869
874
  setProps(props) {
870
875
  this.placement = props.placement ?? this.placement;
876
+ this.viewId = props.viewId ?? this.viewId;
871
877
  super.setProps(props);
872
878
  }
873
879
  onRenderHTML(rootElement) {
@@ -958,7 +964,7 @@ var __exports__ = (() => {
958
964
  id: "scale",
959
965
  placement: "bottom-left",
960
966
  label: "Scale",
961
- onCapture: void 0
967
+ viewId: null
962
968
  };
963
969
  function getMetersPerPixel(latitude, zoom) {
964
970
  const earthCircumference = 40075016686e-3;
@@ -1456,17 +1462,16 @@ var __exports__ = (() => {
1456
1462
  }
1457
1463
  };
1458
1464
  this.viewports = {};
1459
- this.placement = this.props.placement ?? this.placement;
1460
1465
  this.setProps(this.props);
1461
- this.geocoder = getGeocoder(this.props);
1462
1466
  }
1463
1467
  setProps(props) {
1464
- super.setProps(props);
1465
1468
  this.placement = props.placement ?? this.placement;
1469
+ this.viewId = props.viewId ?? this.viewId;
1466
1470
  this.geocoder = getGeocoder(this.props);
1467
1471
  if (this.geocoder.requiresApiKey && !this.props.apiKey) {
1468
1472
  throw new Error(`API key is required for the ${this.geocoder.name} geocoder`);
1469
1473
  }
1474
+ super.setProps(props);
1470
1475
  }
1471
1476
  onRenderHTML(rootElement) {
1472
1477
  const menuItems = this.props._geolocation ? [CURRENT_LOCATION2, ...this.geocodeHistory.addressHistory] : [...this.geocodeHistory.addressHistory];
@@ -1541,7 +1546,7 @@ var __exports__ = (() => {
1541
1546
  GeocoderWidget.defaultProps = {
1542
1547
  ...import_core7.Widget.defaultProps,
1543
1548
  id: "geocoder",
1544
- viewId: void 0,
1549
+ viewId: null,
1545
1550
  placement: "top-left",
1546
1551
  label: "Geocoder",
1547
1552
  transitionDuration: 200,
@@ -1578,7 +1583,7 @@ var __exports__ = (() => {
1578
1583
  this.className = "deck-widget-fullscreen";
1579
1584
  this.placement = "top-left";
1580
1585
  this.fullscreen = false;
1581
- this.placement = props.placement ?? this.placement;
1586
+ this.setProps(this.props);
1582
1587
  }
1583
1588
  onAdd() {
1584
1589
  document.addEventListener("fullscreenchange", this.onFullscreenChange.bind(this));
@@ -1591,7 +1596,9 @@ var __exports__ = (() => {
1591
1596
  /* @__PURE__ */ u2(
1592
1597
  IconButton,
1593
1598
  {
1594
- onClick: this.handleClick.bind(this),
1599
+ onClick: () => {
1600
+ this.handleClick().catch((err) => import_core9.log.error(err)());
1601
+ },
1595
1602
  label: this.fullscreen ? this.props.exitLabel : this.props.enterLabel,
1596
1603
  className: this.fullscreen ? "deck-widget-fullscreen-exit" : "deck-widget-fullscreen-enter"
1597
1604
  }
@@ -1601,6 +1608,7 @@ var __exports__ = (() => {
1601
1608
  }
1602
1609
  setProps(props) {
1603
1610
  this.placement = props.placement ?? this.placement;
1611
+ this.viewId = props.viewId ?? this.viewId;
1604
1612
  super.setProps(props);
1605
1613
  }
1606
1614
  getContainer() {
@@ -1646,351 +1654,344 @@ var __exports__ = (() => {
1646
1654
  ...import_core9.Widget.defaultProps,
1647
1655
  id: "fullscreen",
1648
1656
  placement: "top-left",
1657
+ viewId: null,
1649
1658
  enterLabel: "Enter Fullscreen",
1650
1659
  exitLabel: "Exit Fullscreen",
1651
1660
  container: void 0
1652
1661
  };
1653
1662
 
1654
- // src/screenshot-widget.tsx
1663
+ // src/splitter-widget.tsx
1655
1664
  var import_core10 = __toESM(require_core(), 1);
1656
- var _ScreenshotWidget = class extends import_core10.Widget {
1657
- constructor(props = {}) {
1658
- super(props, _ScreenshotWidget.defaultProps);
1659
- this.className = "deck-widget-screenshot";
1660
- this.placement = "top-left";
1661
- this.placement = props.placement ?? this.placement;
1665
+ var _SplitterWidget = class extends import_core10.Widget {
1666
+ constructor(props) {
1667
+ super(props, _SplitterWidget.defaultProps);
1668
+ this.className = "deck-widget-splitter";
1669
+ this.placement = "fill";
1662
1670
  }
1663
1671
  setProps(props) {
1664
- this.placement = props.placement ?? this.placement;
1665
1672
  super.setProps(props);
1666
1673
  }
1667
1674
  onRenderHTML(rootElement) {
1675
+ rootElement.style.position = "absolute";
1676
+ rootElement.style.top = "0";
1677
+ rootElement.style.left = "0";
1678
+ rootElement.style.width = "100%";
1679
+ rootElement.style.height = "100%";
1680
+ rootElement.style.margin = "0px";
1668
1681
  B(
1669
1682
  /* @__PURE__ */ u2(
1670
- IconButton,
1683
+ Splitter,
1671
1684
  {
1672
- className: "deck-widget-camera",
1673
- label: this.props.label,
1674
- onClick: this.handleClick.bind(this)
1685
+ orientation: this.props.orientation,
1686
+ initialSplit: this.props.initialSplit,
1687
+ onChange: this.props.onChange,
1688
+ onDragStart: this.props.onDragStart,
1689
+ onDragEnd: this.props.onDragEnd
1675
1690
  }
1676
1691
  ),
1677
1692
  rootElement
1678
1693
  );
1679
1694
  }
1680
- handleClick() {
1681
- if (this.props.onCapture) {
1682
- this.props.onCapture(this);
1683
- return;
1684
- }
1685
- const dataURL = this.captureScreenToDataURL(this.props.imageFormat);
1686
- if (dataURL) {
1687
- this.downloadDataURL(dataURL, this.props.filename);
1688
- }
1689
- }
1690
- /** @note only captures canvas contents, not HTML DOM or CSS styles */
1691
- captureScreenToDataURL(imageFormat) {
1692
- const canvas = this.deck?.getCanvas();
1693
- return canvas?.toDataURL(imageFormat);
1694
- }
1695
- /** Download a data URL */
1696
- downloadDataURL(dataURL, filename) {
1697
- const link = document.createElement("a");
1698
- link.href = dataURL;
1699
- link.download = filename;
1700
- link.click();
1701
- }
1702
1695
  };
1703
- var ScreenshotWidget = _ScreenshotWidget;
1704
- ScreenshotWidget.defaultProps = {
1696
+ var SplitterWidget = _SplitterWidget;
1697
+ SplitterWidget.defaultProps = {
1705
1698
  ...import_core10.Widget.defaultProps,
1706
- id: "screenshot",
1707
- placement: "top-left",
1708
- label: "Screenshot",
1709
- filename: "screenshot.png",
1710
- imageFormat: "image/png",
1711
- onCapture: void 0
1699
+ id: "splitter-widget",
1700
+ viewId1: "",
1701
+ viewId2: "",
1702
+ orientation: "vertical",
1703
+ initialSplit: 0.5,
1704
+ onChange: () => {
1705
+ },
1706
+ onDragStart: () => {
1707
+ },
1708
+ onDragEnd: () => {
1709
+ }
1712
1710
  };
1711
+ function Splitter({
1712
+ orientation,
1713
+ initialSplit,
1714
+ onChange,
1715
+ onDragStart,
1716
+ onDragEnd
1717
+ }) {
1718
+ const [split, setSplit] = h2(initialSplit);
1719
+ const dragging = A2(false);
1720
+ const containerRef = A2(null);
1721
+ const handleDragStart = (event) => {
1722
+ dragging.current = true;
1723
+ onDragStart?.();
1724
+ document.addEventListener("mousemove", handleDragging);
1725
+ document.addEventListener("mouseup", handleDragEnd);
1726
+ event.preventDefault();
1727
+ };
1728
+ const handleDragging = (event) => {
1729
+ if (!dragging.current || !containerRef.current)
1730
+ return;
1731
+ const rect = containerRef.current.getBoundingClientRect();
1732
+ let newSplit;
1733
+ if (orientation === "vertical") {
1734
+ newSplit = (event.clientX - rect.left) / rect.width;
1735
+ } else {
1736
+ newSplit = (event.clientY - rect.top) / rect.height;
1737
+ }
1738
+ newSplit = Math.min(Math.max(newSplit, 0.05), 0.95);
1739
+ setSplit(newSplit);
1740
+ onChange?.(newSplit);
1741
+ };
1742
+ const handleDragEnd = (event) => {
1743
+ if (!dragging.current)
1744
+ return;
1745
+ dragging.current = false;
1746
+ onDragEnd?.();
1747
+ document.removeEventListener("mousemove", handleDragging);
1748
+ document.removeEventListener("mouseup", handleDragEnd);
1749
+ };
1750
+ const splitterStyle = orientation === "vertical" ? {
1751
+ position: "absolute",
1752
+ top: 0,
1753
+ bottom: 0,
1754
+ left: `${split * 100}%`,
1755
+ width: "4px",
1756
+ cursor: "col-resize",
1757
+ background: "#ccc",
1758
+ zIndex: 10,
1759
+ pointerEvents: "auto",
1760
+ boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
1761
+ } : {
1762
+ position: "absolute",
1763
+ left: 0,
1764
+ right: 0,
1765
+ top: `${split * 100}%`,
1766
+ height: "4px",
1767
+ cursor: "row-resize",
1768
+ background: "#ccc",
1769
+ zIndex: 10,
1770
+ pointerEvents: "auto",
1771
+ boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
1772
+ };
1773
+ const containerStyle = {
1774
+ position: "absolute",
1775
+ top: 0,
1776
+ left: 0,
1777
+ right: 0,
1778
+ bottom: 0
1779
+ };
1780
+ return /* @__PURE__ */ u2("div", { ref: containerRef, style: containerStyle, children: /* @__PURE__ */ u2(
1781
+ "div",
1782
+ {
1783
+ style: splitterStyle,
1784
+ onMouseDown: handleDragStart
1785
+ }
1786
+ ) });
1787
+ }
1713
1788
 
1714
- // src/loading-widget.tsx
1789
+ // src/view-selector-widget.tsx
1715
1790
  var import_core11 = __toESM(require_core(), 1);
1716
- var _LoadingWidget = class extends import_core11.Widget {
1791
+
1792
+ // src/lib/components/icon-menu.tsx
1793
+ function IconMenu(props) {
1794
+ const [menuOpen, setMenuOpen] = h2(false);
1795
+ const containerRef = A2(null);
1796
+ const handleClickOutside = (event) => {
1797
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1798
+ setMenuOpen(false);
1799
+ }
1800
+ };
1801
+ y2(() => {
1802
+ document.addEventListener("mousedown", handleClickOutside);
1803
+ return () => {
1804
+ document.removeEventListener("mousedown", handleClickOutside);
1805
+ };
1806
+ }, [containerRef]);
1807
+ const [selectedItem, setSelectedItem] = h2(props.initialItem);
1808
+ const handleSelectItem = (item) => {
1809
+ setSelectedItem(item);
1810
+ setMenuOpen(false);
1811
+ props.onItemSelected(item);
1812
+ };
1813
+ const handleButtonClick = () => setMenuOpen(!menuOpen);
1814
+ const selectedMenuItem = props.menuItems.find((item) => item.value === selectedItem);
1815
+ const label = props.label || selectedMenuItem?.label || "";
1816
+ const icon = props.icon || selectedMenuItem?.icon;
1817
+ return /* @__PURE__ */ u2("div", { style: { position: "relative", display: "inline-block" }, ref: containerRef, children: [
1818
+ /* @__PURE__ */ u2(IconButton, { className: props.className, label, onClick: handleButtonClick, children: icon }),
1819
+ menuOpen && /* @__PURE__ */ u2("div", { className: "deck-widget-icon-menu", children: /* @__PURE__ */ u2(ButtonGroup, { orientation: "vertical", children: props.menuItems.map((item) => /* @__PURE__ */ u2(
1820
+ GroupedIconButton,
1821
+ {
1822
+ label: item.label,
1823
+ onClick: () => handleSelectItem(item.value),
1824
+ children: item.icon
1825
+ },
1826
+ item.value
1827
+ )) }) })
1828
+ ] });
1829
+ }
1830
+
1831
+ // src/view-selector-widget.tsx
1832
+ var _ViewSelectorWidget = class extends import_core11.Widget {
1717
1833
  constructor(props = {}) {
1718
- super(props, _LoadingWidget.defaultProps);
1719
- this.className = "deck-widget-loading";
1834
+ super(props, _ViewSelectorWidget.defaultProps);
1835
+ this.className = "deck-widget-view-selector";
1720
1836
  this.placement = "top-left";
1721
- this.loading = true;
1722
- this.placement = props.placement ?? this.placement;
1837
+ this.handleSelectMode = (viewMode) => {
1838
+ this.viewMode = viewMode;
1839
+ this.updateHTML();
1840
+ this.props.onViewModeChange(viewMode);
1841
+ };
1842
+ this.viewMode = this.props.initialViewMode;
1843
+ this.setProps(this.props);
1723
1844
  }
1724
1845
  setProps(props) {
1725
1846
  this.placement = props.placement ?? this.placement;
1847
+ this.viewId = props.viewId ?? this.viewId;
1726
1848
  super.setProps(props);
1727
1849
  }
1728
1850
  onRenderHTML(rootElement) {
1729
1851
  B(
1730
- // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component.
1731
- this.loading && /* @__PURE__ */ u2(
1732
- IconButton,
1852
+ /* @__PURE__ */ u2(
1853
+ IconMenu,
1733
1854
  {
1734
- className: "deck-widget-spinner-icon",
1735
- label: this.props.label,
1736
- onClick: this.handleClick.bind(this)
1855
+ className: "deck-widget-view-selector",
1856
+ menuItems: MENU_ITEMS.map((item) => ({
1857
+ ...item,
1858
+ icon: item.icon()
1859
+ })),
1860
+ initialItem: this.props.initialViewMode,
1861
+ onItemSelected: this.handleSelectMode
1737
1862
  }
1738
1863
  ),
1739
1864
  rootElement
1740
1865
  );
1741
1866
  }
1742
- onRedraw({ layers }) {
1743
- const loading = !layers.some((layer) => !layer.isLoaded);
1744
- if (loading !== this.loading) {
1745
- this.loading = loading;
1746
- this.updateHTML();
1747
- }
1748
- }
1749
- // TODO(ibgreen) - this should not be a button, see above.
1750
- handleClick() {
1751
- }
1752
1867
  };
1753
- var LoadingWidget = _LoadingWidget;
1754
- LoadingWidget.defaultProps = {
1868
+ var ViewSelectorWidget = _ViewSelectorWidget;
1869
+ ViewSelectorWidget.defaultProps = {
1755
1870
  ...import_core11.Widget.defaultProps,
1756
- id: "loading",
1871
+ id: "view-selector",
1757
1872
  placement: "top-left",
1758
- label: "Loading layer data"
1873
+ viewId: null,
1874
+ label: "Split View",
1875
+ initialViewMode: "single",
1876
+ onViewModeChange: () => {
1877
+ }
1878
+ };
1879
+ var ICON_STYLE = { width: "24px", height: "24px" };
1880
+ var ICONS = {
1881
+ single: () => /* @__PURE__ */ u2("svg", { width: "24", height: "24", style: ICON_STYLE, children: /* @__PURE__ */ u2(
1882
+ "rect",
1883
+ {
1884
+ x: "4",
1885
+ y: "4",
1886
+ width: "16",
1887
+ height: "16",
1888
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
1889
+ fill: "none",
1890
+ strokeWidth: "2"
1891
+ }
1892
+ ) }),
1893
+ "split-horizontal": () => /* @__PURE__ */ u2("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
1894
+ /* @__PURE__ */ u2(
1895
+ "rect",
1896
+ {
1897
+ x: "4",
1898
+ y: "4",
1899
+ width: "16",
1900
+ height: "7",
1901
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
1902
+ fill: "none",
1903
+ strokeWidth: "2"
1904
+ }
1905
+ ),
1906
+ /* @__PURE__ */ u2(
1907
+ "rect",
1908
+ {
1909
+ x: "4",
1910
+ y: "13",
1911
+ width: "16",
1912
+ height: "7",
1913
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
1914
+ fill: "none",
1915
+ strokeWidth: "2"
1916
+ }
1917
+ )
1918
+ ] }),
1919
+ "split-vertical": () => /* @__PURE__ */ u2("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
1920
+ /* @__PURE__ */ u2(
1921
+ "rect",
1922
+ {
1923
+ x: "4",
1924
+ y: "4",
1925
+ width: "7",
1926
+ height: "16",
1927
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
1928
+ fill: "none",
1929
+ strokeWidth: "2"
1930
+ }
1931
+ ),
1932
+ /* @__PURE__ */ u2(
1933
+ "rect",
1934
+ {
1935
+ x: "13",
1936
+ y: "4",
1937
+ width: "7",
1938
+ height: "16",
1939
+ stroke: "var(--button-icon-hover, rgb(24, 24, 26))",
1940
+ fill: "none",
1941
+ strokeWidth: "2"
1942
+ }
1943
+ )
1944
+ ] })
1759
1945
  };
1946
+ var MENU_ITEMS = [
1947
+ { value: "single", icon: ICONS.single, label: "Single View" },
1948
+ { value: "split-horizontal", icon: ICONS["split-horizontal"], label: "Split Horizontal" },
1949
+ { value: "split-vertical", icon: ICONS["split-vertical"], label: "Split Vertical" }
1950
+ ];
1760
1951
 
1761
- // src/fps-widget.tsx
1952
+ // src/info-widget.tsx
1762
1953
  var import_core12 = __toESM(require_core(), 1);
1763
- var _FpsWidget = class extends import_core12.Widget {
1764
- constructor(props = {}) {
1765
- super(props, _FpsWidget.defaultProps);
1766
- this.className = "deck-widget-fps";
1767
- this.placement = "top-left";
1768
- this._lastFps = -1;
1769
- this.placement = props.placement ?? this.placement;
1954
+ var _InfoWidget = class extends import_core12.Widget {
1955
+ constructor(props) {
1956
+ super(props, _InfoWidget.defaultProps);
1957
+ this.className = "deck-widget-info";
1958
+ this.placement = "fill";
1959
+ this.setProps(this.props);
1770
1960
  }
1771
1961
  setProps(props) {
1772
- if (props.placement) {
1773
- this.placement = props.placement;
1774
- }
1962
+ this.viewId = props.viewId ?? this.viewId;
1775
1963
  super.setProps(props);
1776
1964
  }
1777
- onAdd({}) {
1778
- this._lastFps = this._getFps();
1965
+ onCreateRootElement() {
1966
+ const element = super.onCreateRootElement();
1967
+ const style = { margin: "0px", top: "0px", left: "0px", position: "absolute" };
1968
+ Object.entries(style).forEach(([key, value]) => element.style.setProperty(key, value));
1969
+ return element;
1970
+ }
1971
+ onViewportChange(viewport) {
1972
+ this.viewport = viewport;
1779
1973
  this.updateHTML();
1780
1974
  }
1781
- onRenderHTML(rootElement) {
1782
- const fps = this._getFps();
1783
- rootElement.innerText = `FPS:
1784
- ${fps}`;
1785
- rootElement.style.backgroundColor = "white";
1786
- rootElement.style.fontFamily = "monospace";
1787
- rootElement.style.fontSize = "8px";
1788
- rootElement.style.fontWeight = "700";
1789
- }
1790
- onRedraw({}) {
1791
- const fps = this._getFps();
1792
- if (fps !== this._lastFps) {
1793
- this._lastFps = fps;
1794
- this.updateHTML();
1975
+ onHover(info) {
1976
+ if (this.props.mode === "hover" && this.props.getTooltip) {
1977
+ const tooltip = this.props.getTooltip(info, this);
1978
+ this.setProps({
1979
+ visible: tooltip !== null,
1980
+ ...tooltip,
1981
+ style: { zIndex: "1", ...tooltip?.style }
1982
+ });
1795
1983
  }
1796
1984
  }
1797
- _getFps() {
1798
- return Math.round(this.deck.metrics.fps ?? 0);
1799
- }
1800
- };
1801
- var FpsWidget = _FpsWidget;
1802
- FpsWidget.defaultProps = {
1803
- ...import_core12.Widget.defaultProps,
1804
- id: "fps",
1805
- placement: "top-left"
1806
- };
1807
-
1808
- // src/theme-widget.tsx
1809
- var import_core13 = __toESM(require_core(), 1);
1810
- var import_core14 = __toESM(require_core(), 1);
1811
-
1812
- // src/themes.ts
1813
- var LightTheme = {
1814
- "--widget-margin": "12px",
1815
- "--button-size": "28px",
1816
- "--button-corner-radius": "8px",
1817
- "--button-background": "#fff",
1818
- "--button-stroke": "rgba(255, 255, 255, 0.3)",
1819
- "--button-inner-stroke": "unset",
1820
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
1821
- "--button-backdrop-filter": "unset",
1822
- "--button-icon-idle": "rgba(97, 97, 102, 1)",
1823
- "--button-icon-hover": "rgba(24, 24, 26, 1)",
1824
- "--icon-compass-north-color": "rgb(240, 92, 68)",
1825
- "--icon-compass-south-color": "rgb(204, 204, 204)"
1826
- };
1827
- var DarkTheme = {
1828
- "--widget-margin": "12px",
1829
- "--button-size": "28px",
1830
- "--button-corner-radius": "8px",
1831
- "--button-background": "rgba(18, 18, 20, 1)",
1832
- "--button-stroke": "rgba(18, 18, 20, 0.30)",
1833
- "--button-inner-stroke": "unset",
1834
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
1835
- "--button-backdrop-filter": "unset",
1836
- "--button-icon-idle": "rgba(158, 157, 168, 1)",
1837
- "--button-icon-hover": "rgba(215, 214, 229, 1)",
1838
- "--icon-compass-north-color": "rgb(240, 92, 68)",
1839
- "--icon-compass-south-color": "rgb(200, 199, 209)"
1840
- };
1841
- var LightGlassTheme = {
1842
- "--widget-margin": "12px",
1843
- "--button-size": "28px",
1844
- "--button-corner-radius": "8px",
1845
- "--button-background": "rgba(255, 255, 255, 0.6)",
1846
- "--button-stroke": "rgba(255, 255, 255, 0.3)",
1847
- "--button-inner-stroke": "1px solid rgba(255, 255, 255, 0.6)",
1848
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
1849
- "--button-backdrop-filter": "blur(4px)",
1850
- "--button-icon-idle": "rgba(97, 97, 102, 1)",
1851
- "--button-icon-hover": "rgba(24, 24, 26, 1)",
1852
- "--icon-compass-north-color": "rgb(240, 92, 68)",
1853
- "--icon-compass-south-color": "rgb(204, 204, 204)"
1854
- };
1855
- var DarkGlassTheme = {
1856
- "--widget-margin": "12px",
1857
- "--button-size": "28px",
1858
- "--button-corner-radius": "8px",
1859
- "--button-background": "rgba(18, 18, 20, 0.75)",
1860
- "--button-stroke": "rgba(18, 18, 20, 0.30)",
1861
- "--button-inner-stroke": "1px solid rgba(18, 18, 20, 0.75)",
1862
- "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
1863
- "--button-backdrop-filter": "blur(4px)",
1864
- "--button-icon-idle": "rgba(158, 157, 168, 1)",
1865
- "--button-icon-hover": "rgba(215, 214, 229, 1)",
1866
- "--icon-compass-north-color": "rgb(240, 92, 68)",
1867
- "--icon-compass-south-color": "rgb(200, 199, 209)"
1868
- };
1869
-
1870
- // src/theme-widget.tsx
1871
- var _ThemeWidget = class extends import_core14.Widget {
1872
- constructor(props = {}) {
1873
- super(props, _ThemeWidget.defaultProps);
1874
- this.className = "deck-widget-theme";
1875
- this.placement = "top-left";
1876
- this.themeMode = "dark";
1877
- this.placement = props.placement ?? this.placement;
1878
- this.themeMode = this._getInitialThemeMode();
1879
- }
1880
- // eslint-disable-next-line complexity
1881
- setProps(props) {
1882
- const { lightModeTheme, darkModeTheme } = this.props;
1883
- this.placement = props.placement ?? this.placement;
1884
- super.setProps(props);
1885
- switch (this.themeMode) {
1886
- case "light":
1887
- if (props.lightModeTheme && !(0, import_core13._deepEqual)(props.lightModeTheme, lightModeTheme, 1)) {
1888
- this._setThemeMode("light");
1889
- }
1890
- break;
1891
- case "dark":
1892
- if (props.darkModeTheme && !(0, import_core13._deepEqual)(props.darkModeTheme, darkModeTheme, 1)) {
1893
- this._setThemeMode("dark");
1894
- }
1895
- break;
1896
- }
1897
- }
1898
- onRenderHTML(rootElement) {
1899
- const { lightModeLabel, darkModeLabel } = this.props;
1900
- B(
1901
- /* @__PURE__ */ u2(
1902
- IconButton,
1903
- {
1904
- onClick: this._handleClick.bind(this),
1905
- label: this.themeMode === "dark" ? darkModeLabel : lightModeLabel,
1906
- className: this.themeMode === "dark" ? "deck-widget-moon" : "deck-widget-sun"
1907
- }
1908
- ),
1909
- rootElement
1910
- );
1911
- }
1912
- onAdd() {
1913
- this._setThemeMode(this.themeMode);
1914
- }
1915
- _handleClick() {
1916
- const newThemeMode = this.themeMode === "dark" ? "light" : "dark";
1917
- this._setThemeMode(newThemeMode);
1918
- }
1919
- _setThemeMode(themeMode) {
1920
- this.themeMode = themeMode;
1921
- const container = this.rootElement?.closest(".deck-widget-container");
1922
- if (container) {
1923
- const themeStyle = themeMode === "dark" ? this.props.darkModeTheme : this.props.lightModeTheme;
1924
- (0, import_core13._applyStyles)(container, themeStyle);
1925
- const label = this.themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
1926
- import_core13.log.log(1, `Switched theme to ${label}`, themeStyle);
1927
- this.updateHTML();
1928
- }
1929
- }
1930
- /** Read browser preference */
1931
- _getInitialThemeMode() {
1932
- const { initialThemeMode } = this.props;
1933
- return initialThemeMode === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : initialThemeMode;
1934
- }
1935
- };
1936
- var ThemeWidget = _ThemeWidget;
1937
- ThemeWidget.defaultProps = {
1938
- ...import_core14.Widget.defaultProps,
1939
- id: "theme",
1940
- placement: "top-left",
1941
- lightModeLabel: "Light Mode",
1942
- lightModeTheme: LightGlassTheme,
1943
- darkModeLabel: "Dark Mode",
1944
- darkModeTheme: DarkGlassTheme,
1945
- initialThemeMode: "auto"
1946
- };
1947
-
1948
- // src/info-widget.tsx
1949
- var import_core15 = __toESM(require_core(), 1);
1950
- var _InfoWidget = class extends import_core15.Widget {
1951
- constructor(props) {
1952
- super(props, _InfoWidget.defaultProps);
1953
- this.className = "deck-widget-info";
1954
- this.placement = "fill";
1955
- this.viewId = null;
1956
- props.position = props.position || [0, 0];
1957
- props.text = props.text || "";
1958
- props.visible = props.visible || false;
1959
- props.minOffset = props.minOffset ?? 0;
1960
- }
1961
- setProps(props) {
1962
- super.setProps(props);
1963
- }
1964
- onCreateRootElement() {
1965
- const element = super.onCreateRootElement();
1966
- const style = { margin: "0px", top: "0px", left: "0px", position: "absolute" };
1967
- Object.entries(style).forEach(([key, value]) => element.style.setProperty(key, value));
1968
- return element;
1969
- }
1970
- onViewportChange(viewport) {
1971
- this.viewport = viewport;
1972
- this.updateHTML();
1973
- }
1974
- onHover(info) {
1975
- if (this.props.mode === "hover" && this.props.getTooltip) {
1976
- const tooltip = this.props.getTooltip(info, this);
1977
- this.setProps({
1978
- visible: tooltip !== null,
1979
- ...tooltip,
1980
- style: { zIndex: 1, ...tooltip?.style }
1981
- });
1982
- }
1983
- }
1984
- onClick(info) {
1985
- if (this.props.mode === "click" && this.props.getTooltip) {
1986
- const tooltip = this.props.getTooltip(info, this);
1987
- this.setProps({
1988
- visible: tooltip !== null,
1989
- ...tooltip
1990
- });
1991
- return tooltip !== null;
1992
- }
1993
- return this.props.onClick?.(this, info) || false;
1985
+ onClick(info) {
1986
+ if (this.props.mode === "click" && this.props.getTooltip) {
1987
+ const tooltip = this.props.getTooltip(info, this);
1988
+ this.setProps({
1989
+ visible: tooltip !== null,
1990
+ ...tooltip
1991
+ });
1992
+ return tooltip !== null;
1993
+ }
1994
+ return this.props.onClick?.(this, info) || false;
1994
1995
  }
1995
1996
  onAdd({ deck, viewId }) {
1996
1997
  this.deck = deck;
@@ -2089,11 +2090,11 @@ ${fps}`;
2089
2090
  };
2090
2091
  var InfoWidget = _InfoWidget;
2091
2092
  InfoWidget.defaultProps = {
2092
- ...import_core15.Widget.defaultProps,
2093
+ ...import_core12.Widget.defaultProps,
2094
+ id: "info",
2093
2095
  position: [0, 0],
2094
2096
  text: "",
2095
2097
  visible: false,
2096
- // Set default minOffset if not provided
2097
2098
  minOffset: 0,
2098
2099
  viewId: null,
2099
2100
  mode: "hover",
@@ -2101,188 +2102,57 @@ ${fps}`;
2101
2102
  onClick: void 0
2102
2103
  };
2103
2104
 
2104
- // src/stats-widget.tsx
2105
- var import_core16 = __toESM(require_core(), 1);
2106
- var import_core17 = __toESM(require_core2(), 1);
2107
- var RIGHT_ARROW = "\u25B6";
2108
- var DOWN_ARROW = "\u2B07";
2109
- var DEFAULT_COUNT_FORMATTER = (stat) => `${stat.name}: ${stat.count}`;
2110
- function formatTime(time) {
2111
- return time < 1e3 ? `${time.toFixed(2)}ms` : `${(time / 1e3).toFixed(2)}s`;
2112
- }
2113
- function formatMemory(bytes) {
2114
- const mb = bytes / 1e6;
2115
- return `${mb.toFixed(1)} MB`;
2116
- }
2117
- var DEFAULT_FORMATTERS = {
2118
- count: DEFAULT_COUNT_FORMATTER,
2119
- averageTime: (stat) => `${stat.name}: ${formatTime(stat.getAverageTime())}`,
2120
- totalTime: (stat) => `${stat.name}: ${formatTime(stat.time)}`,
2121
- fps: (stat) => `${stat.name}: ${Math.round(stat.getHz())}fps`,
2122
- memory: (stat) => `${stat.name}: ${formatMemory(stat.count)}`
2105
+ // src/context-menu-widget.tsx
2106
+ var import_core13 = __toESM(require_core(), 1);
2107
+
2108
+ // src/lib/components/simple-menu.tsx
2109
+ var MENU_STYLE = {
2110
+ position: "absolute",
2111
+ top: "100%",
2112
+ left: 0,
2113
+ background: "white",
2114
+ border: "1px solid #ccc",
2115
+ borderRadius: "4px",
2116
+ marginTop: "var(--menu-gap, 4px)",
2117
+ zIndex: 100
2123
2118
  };
2124
- var _StatsWidget = class extends import_core16.Widget {
2125
- constructor(props = {}) {
2126
- super(props, _StatsWidget.defaultProps);
2127
- this.className = "deck-widget-stats";
2128
- this.placement = "top-left";
2129
- this._counter = 0;
2130
- this.collapsed = true;
2131
- this._toggleCollapsed = () => {
2132
- this.collapsed = !this.collapsed;
2133
- this.updateHTML();
2134
- };
2135
- this._formatters = { ...DEFAULT_FORMATTERS };
2136
- this.setProps(props);
2137
- this._resetOnUpdate = { ...this.props.resetOnUpdate };
2138
- this._stats = this.props.stats;
2139
- }
2140
- setProps(props) {
2141
- super.setProps(props);
2142
- this._stats = this._getStats();
2143
- if (props.formatters) {
2144
- for (const name in props.formatters) {
2145
- const f4 = props.formatters[name];
2146
- this._formatters[name] = typeof f4 === "string" ? DEFAULT_FORMATTERS[f4] || DEFAULT_COUNT_FORMATTER : f4;
2147
- }
2148
- }
2149
- if (props.resetOnUpdate) {
2150
- this._resetOnUpdate = { ...props.resetOnUpdate };
2151
- }
2152
- }
2153
- onAdd() {
2154
- this._stats = this._getStats();
2155
- this.updateHTML();
2156
- }
2157
- onRenderHTML(rootElement) {
2158
- const stats = this._stats;
2159
- const collapsed = this.collapsed;
2160
- const title = this.props.title || stats?.id || "Stats";
2161
- const items = [];
2162
- if (!collapsed && stats) {
2163
- stats.forEach((stat) => {
2164
- const lines = this._getLines(stat);
2165
- if (this._resetOnUpdate && this._resetOnUpdate[stat.name]) {
2166
- stat.reset();
2167
- }
2168
- lines.forEach((line, i4) => {
2169
- items.push(
2170
- /* @__PURE__ */ u2("div", { style: { whiteSpace: "pre" }, children: line }, `${stat.name}-${i4}`)
2171
- );
2172
- });
2173
- });
2174
- }
2175
- B(
2176
- /* @__PURE__ */ u2("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [
2177
- /* @__PURE__ */ u2(
2178
- "div",
2179
- {
2180
- className: "deck-widget-stats-header",
2181
- style: { cursor: "pointer", pointerEvents: "auto" },
2182
- onClick: this._toggleCollapsed,
2183
- children: [
2184
- collapsed ? RIGHT_ARROW : DOWN_ARROW,
2185
- " ",
2186
- title
2187
- ]
2188
- }
2189
- ),
2190
- !collapsed && /* @__PURE__ */ u2("div", { className: "deck-widget-stats-content deck-widget-common", children: items })
2191
- ] }),
2192
- rootElement
2193
- );
2194
- }
2195
- onRedraw() {
2196
- const framesPerUpdate = Math.max(1, this.props.framesPerUpdate || 1);
2197
- if (this._counter++ % framesPerUpdate === 0) {
2198
- console.log("Redrawing stats widget");
2199
- this.updateHTML();
2200
- }
2201
- }
2202
- _getStats() {
2203
- switch (this.props.type) {
2204
- case "deck":
2205
- return this.deck?.stats;
2206
- case "luma":
2207
- return Array.from(import_core17.luma.stats.stats.values())[0];
2208
- case "device":
2209
- const device = this.deck?.device;
2210
- const stats = device?.statsManager.stats.values();
2211
- return stats ? Array.from(stats)[0] : void 0;
2212
- case "custom":
2213
- return this.props.stats;
2214
- default:
2215
- throw new Error(`Unknown stats type: ${this.props.type}`);
2216
- }
2217
- }
2218
- _getLines(stat) {
2219
- const formatter = this._formatters[stat.name] || this._formatters[stat.type || ""] || DEFAULT_COUNT_FORMATTER;
2220
- return formatter(stat).split("\n");
2221
- }
2222
- };
2223
- var StatsWidget = _StatsWidget;
2224
- StatsWidget.defaultProps = {
2225
- ...import_core16.Widget.defaultProps,
2226
- type: "deck",
2227
- stats: void 0,
2228
- title: "Stats",
2229
- framesPerUpdate: 1,
2230
- formatters: {},
2231
- resetOnUpdate: {},
2232
- id: "stats"
2233
- };
2234
-
2235
- // src/context-menu-widget.tsx
2236
- var import_core18 = __toESM(require_core(), 1);
2237
-
2238
- // src/lib/components/simple-menu.tsx
2239
- var MENU_STYLE = {
2240
- position: "absolute",
2241
- top: "100%",
2242
- left: 0,
2243
- background: "white",
2244
- border: "1px solid #ccc",
2245
- borderRadius: "4px",
2246
- marginTop: "4px",
2247
- zIndex: 100
2248
- };
2249
- var MENU_ITEM_STYLE = {
2250
- background: "white",
2251
- border: "none",
2252
- padding: "4px",
2253
- cursor: "pointer",
2254
- pointerEvents: "auto"
2255
- };
2256
- var SimpleMenu = (props) => {
2257
- const { menuItems, onItemSelected, position, style } = props;
2258
- const styleOverride = {
2259
- ...MENU_STYLE,
2260
- ...style,
2261
- left: `${position.x}px`,
2262
- top: `${position.y}px`
2263
- };
2264
- return /* @__PURE__ */ u2("div", { style: styleOverride, children: menuItems.map(({ key, label }) => /* @__PURE__ */ u2(
2265
- "button",
2266
- {
2267
- style: { ...MENU_ITEM_STYLE, display: "block" },
2268
- onClick: (_2) => onItemSelected(key),
2269
- children: label
2270
- },
2271
- key
2272
- )) });
2273
- };
2274
-
2275
- // src/context-menu-widget.tsx
2276
- var MOUSE_BUTTON_RIGHT = 2;
2277
- var MOUSE_WHICH_RIGHT = 3;
2278
- var _ContextMenuWidget = class extends import_core18.Widget {
2279
- constructor(props) {
2280
- super(props, _ContextMenuWidget.defaultProps);
2281
- this.className = "deck-widget-context-menu";
2282
- this.placement = "fill";
2283
- this.pickInfo = null;
2284
- this.pickInfo = null;
2285
- this.setProps(props);
2119
+ var MENU_ITEM_STYLE = {
2120
+ background: "white",
2121
+ border: "none",
2122
+ padding: "4px",
2123
+ cursor: "pointer",
2124
+ pointerEvents: "auto"
2125
+ };
2126
+ var SimpleMenu = (props) => {
2127
+ const { menuItems, onItemSelected, position, style } = props;
2128
+ const styleOverride = {
2129
+ ...MENU_STYLE,
2130
+ ...style,
2131
+ left: `${position.x}px`,
2132
+ top: `${position.y}px`
2133
+ };
2134
+ return /* @__PURE__ */ u2("div", { style: styleOverride, children: menuItems.map(({ key, label }) => /* @__PURE__ */ u2(
2135
+ "button",
2136
+ {
2137
+ style: { ...MENU_ITEM_STYLE, display: "block" },
2138
+ onClick: (_2) => onItemSelected(key),
2139
+ children: label
2140
+ },
2141
+ key
2142
+ )) });
2143
+ };
2144
+
2145
+ // src/context-menu-widget.tsx
2146
+ var MOUSE_BUTTON_RIGHT = 2;
2147
+ var MOUSE_WHICH_RIGHT = 3;
2148
+ var _ContextMenuWidget = class extends import_core13.Widget {
2149
+ constructor(props) {
2150
+ super(props, _ContextMenuWidget.defaultProps);
2151
+ this.className = "deck-widget-context-menu";
2152
+ this.placement = "fill";
2153
+ this.pickInfo = null;
2154
+ this.pickInfo = null;
2155
+ this.setProps(this.props);
2286
2156
  }
2287
2157
  onAdd({ deck }) {
2288
2158
  const element = document.createElement("div");
@@ -2337,7 +2207,7 @@ ${fps}`;
2337
2207
  };
2338
2208
  var ContextMenuWidget = _ContextMenuWidget;
2339
2209
  ContextMenuWidget.defaultProps = {
2340
- ...import_core18.Widget.defaultProps,
2210
+ ...import_core13.Widget.defaultProps,
2341
2211
  id: "context",
2342
2212
  viewId: null,
2343
2213
  visible: false,
@@ -2348,358 +2218,553 @@ ${fps}`;
2348
2218
  onMenuItemSelected: (key, pickInfo) => console.log("Context menu item selected:", key, pickInfo)
2349
2219
  };
2350
2220
 
2351
- // src/splitter-widget.tsx
2352
- var import_core19 = __toESM(require_core(), 1);
2353
- var _SplitterWidget = class extends import_core19.Widget {
2354
- constructor(props) {
2355
- super(props, _SplitterWidget.defaultProps);
2356
- this.className = "deck-widget-splitter";
2357
- this.placement = "fill";
2221
+ // src/timeline-widget.tsx
2222
+ var import_core14 = __toESM(require_core(), 1);
2223
+ var _TimelineWidget = class extends import_core14.Widget {
2224
+ constructor(props = {}) {
2225
+ super(props, _TimelineWidget.defaultProps);
2226
+ this.id = "timeline";
2227
+ this.className = "deck-widget-timeline";
2228
+ this.placement = "bottom-left";
2229
+ this.playing = false;
2230
+ this.timerId = null;
2231
+ this.handlePlayPause = () => {
2232
+ if (this.playing) {
2233
+ this.stop();
2234
+ } else {
2235
+ this.start();
2236
+ }
2237
+ };
2238
+ this.handleSliderChange = (e3) => {
2239
+ const input = e3.target;
2240
+ const val = Number(input.value);
2241
+ this.currentTime = val;
2242
+ this.props.onTimeChange(val);
2243
+ this.updateHTML();
2244
+ };
2245
+ this.tick = () => {
2246
+ const [min, max] = this.props.timeRange;
2247
+ let next = this.currentTime + this.props.step;
2248
+ if (next > max) {
2249
+ next = min;
2250
+ }
2251
+ this.currentTime = next;
2252
+ this.props.onTimeChange(next);
2253
+ this.updateHTML();
2254
+ if (this.playing) {
2255
+ this.timerId = window.setTimeout(this.tick, this.props.playInterval);
2256
+ }
2257
+ };
2258
+ this.currentTime = this.props.initialTime ?? this.props.timeRange[0];
2259
+ this.setProps(this.props);
2358
2260
  }
2359
2261
  setProps(props) {
2262
+ this.placement = props.placement ?? this.placement;
2263
+ this.viewId = props.viewId ?? this.viewId;
2360
2264
  super.setProps(props);
2361
2265
  }
2266
+ onAdd() {
2267
+ this.playing = false;
2268
+ this.timerId = null;
2269
+ }
2270
+ onRemove() {
2271
+ this.stop();
2272
+ }
2362
2273
  onRenderHTML(rootElement) {
2363
- rootElement.style.position = "absolute";
2364
- rootElement.style.top = "0";
2365
- rootElement.style.left = "0";
2366
- rootElement.style.width = "100%";
2367
- rootElement.style.height = "100%";
2368
- rootElement.style.margin = "0px";
2369
2274
  B(
2370
- /* @__PURE__ */ u2(
2371
- Splitter,
2372
- {
2373
- orientation: this.props.orientation,
2374
- initialSplit: this.props.initialSplit,
2375
- onChange: this.props.onChange,
2376
- onDragStart: this.props.onDragStart,
2377
- onDragEnd: this.props.onDragEnd
2378
- }
2379
- ),
2275
+ /* @__PURE__ */ u2("div", { style: { display: "flex", alignItems: "center", pointerEvents: "auto" }, children: [
2276
+ /* @__PURE__ */ u2(IconButton, { label: this.playing ? "Pause" : "Play", onClick: this.handlePlayPause, children: /* @__PURE__ */ u2("div", { className: "text", children: this.playing ? "\u23F8" : "\u25B6" }) }),
2277
+ /* @__PURE__ */ u2(
2278
+ "input",
2279
+ {
2280
+ type: "range",
2281
+ className: "timeline-slider",
2282
+ min: this.props.timeRange[0],
2283
+ max: this.props.timeRange[1],
2284
+ step: this.props.step,
2285
+ value: this.currentTime,
2286
+ onInput: this.handleSliderChange
2287
+ }
2288
+ )
2289
+ ] }),
2380
2290
  rootElement
2381
2291
  );
2382
2292
  }
2293
+ start() {
2294
+ this.playing = true;
2295
+ this.updateHTML();
2296
+ this.tick();
2297
+ }
2298
+ stop() {
2299
+ this.playing = false;
2300
+ if (this.timerId !== null) {
2301
+ window.clearTimeout(this.timerId);
2302
+ this.timerId = null;
2303
+ }
2304
+ this.updateHTML();
2305
+ }
2383
2306
  };
2384
- var SplitterWidget = _SplitterWidget;
2385
- SplitterWidget.defaultProps = {
2386
- ...import_core19.Widget.defaultProps,
2387
- id: "splitter-widget",
2388
- viewId1: "",
2389
- viewId2: "",
2390
- orientation: "vertical",
2391
- initialSplit: 0.5,
2392
- onChange: () => {
2393
- },
2394
- onDragStart: () => {
2307
+ var TimelineWidget = _TimelineWidget;
2308
+ TimelineWidget.defaultProps = {
2309
+ ...import_core14.Widget.defaultProps,
2310
+ id: "timeline",
2311
+ placement: "bottom-left",
2312
+ viewId: null,
2313
+ timeRange: [0, 100],
2314
+ step: 1,
2315
+ initialTime: void 0,
2316
+ onTimeChange: () => {
2395
2317
  },
2396
- onDragEnd: () => {
2397
- }
2318
+ playInterval: 1e3
2398
2319
  };
2399
- function Splitter({
2400
- orientation,
2401
- initialSplit,
2402
- onChange,
2403
- onDragStart,
2404
- onDragEnd
2405
- }) {
2406
- const [split, setSplit] = h2(initialSplit);
2407
- const dragging = A2(false);
2408
- const containerRef = A2(null);
2409
- const handleDragStart = (event) => {
2410
- dragging.current = true;
2411
- onDragStart?.();
2412
- document.addEventListener("mousemove", handleDragging);
2413
- document.addEventListener("mouseup", handleDragEnd);
2414
- event.preventDefault();
2415
- };
2416
- const handleDragging = (event) => {
2417
- if (!dragging.current || !containerRef.current)
2320
+
2321
+ // src/screenshot-widget.tsx
2322
+ var import_core15 = __toESM(require_core(), 1);
2323
+ var _ScreenshotWidget = class extends import_core15.Widget {
2324
+ constructor(props = {}) {
2325
+ super(props, _ScreenshotWidget.defaultProps);
2326
+ this.className = "deck-widget-screenshot";
2327
+ this.placement = "top-left";
2328
+ this.setProps(this.props);
2329
+ }
2330
+ setProps(props) {
2331
+ this.placement = props.placement ?? this.placement;
2332
+ this.viewId = props.viewId ?? this.viewId;
2333
+ super.setProps(props);
2334
+ }
2335
+ onRenderHTML(rootElement) {
2336
+ B(
2337
+ /* @__PURE__ */ u2(
2338
+ IconButton,
2339
+ {
2340
+ className: "deck-widget-camera",
2341
+ label: this.props.label,
2342
+ onClick: this.handleClick.bind(this)
2343
+ }
2344
+ ),
2345
+ rootElement
2346
+ );
2347
+ }
2348
+ handleClick() {
2349
+ if (this.props.onCapture) {
2350
+ this.props.onCapture(this);
2418
2351
  return;
2419
- const rect = containerRef.current.getBoundingClientRect();
2420
- let newSplit;
2421
- if (orientation === "vertical") {
2422
- newSplit = (event.clientX - rect.left) / rect.width;
2423
- } else {
2424
- newSplit = (event.clientY - rect.top) / rect.height;
2425
2352
  }
2426
- newSplit = Math.min(Math.max(newSplit, 0.05), 0.95);
2427
- setSplit(newSplit);
2428
- onChange?.(newSplit);
2429
- };
2430
- const handleDragEnd = (event) => {
2431
- if (!dragging.current)
2432
- return;
2433
- dragging.current = false;
2434
- onDragEnd?.();
2435
- document.removeEventListener("mousemove", handleDragging);
2436
- document.removeEventListener("mouseup", handleDragEnd);
2437
- };
2438
- const splitterStyle = orientation === "vertical" ? {
2439
- position: "absolute",
2440
- top: 0,
2441
- bottom: 0,
2442
- left: `${split * 100}%`,
2443
- width: "4px",
2444
- cursor: "col-resize",
2445
- background: "#ccc",
2446
- zIndex: 10,
2447
- pointerEvents: "auto",
2448
- boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
2449
- } : {
2450
- position: "absolute",
2451
- left: 0,
2452
- right: 0,
2453
- top: `${split * 100}%`,
2454
- height: "4px",
2455
- cursor: "row-resize",
2456
- background: "#ccc",
2457
- zIndex: 10,
2458
- pointerEvents: "auto",
2459
- boxShadow: "inset -1px 0 0 white, inset 1px 0 0 white"
2460
- };
2461
- const containerStyle = {
2462
- position: "absolute",
2463
- top: 0,
2464
- left: 0,
2465
- right: 0,
2466
- bottom: 0
2467
- };
2468
- return /* @__PURE__ */ u2("div", { ref: containerRef, style: containerStyle, children: /* @__PURE__ */ u2(
2469
- "div",
2470
- {
2471
- style: splitterStyle,
2472
- onMouseDown: handleDragStart
2353
+ const dataURL = this.captureScreenToDataURL(this.props.imageFormat);
2354
+ if (dataURL) {
2355
+ this.downloadDataURL(dataURL, this.props.filename);
2473
2356
  }
2474
- ) });
2475
- }
2357
+ }
2358
+ /** @note only captures canvas contents, not HTML DOM or CSS styles */
2359
+ captureScreenToDataURL(imageFormat) {
2360
+ const canvas = this.deck?.getCanvas();
2361
+ return canvas?.toDataURL(imageFormat);
2362
+ }
2363
+ /** Download a data URL */
2364
+ downloadDataURL(dataURL, filename) {
2365
+ const link = document.createElement("a");
2366
+ link.href = dataURL;
2367
+ link.download = filename;
2368
+ link.click();
2369
+ }
2370
+ };
2371
+ var ScreenshotWidget = _ScreenshotWidget;
2372
+ ScreenshotWidget.defaultProps = {
2373
+ ...import_core15.Widget.defaultProps,
2374
+ id: "screenshot",
2375
+ placement: "top-left",
2376
+ viewId: null,
2377
+ label: "Screenshot",
2378
+ filename: "screenshot.png",
2379
+ imageFormat: "image/png",
2380
+ onCapture: void 0
2381
+ };
2476
2382
 
2477
- // src/timeline-widget.tsx
2478
- var import_core20 = __toESM(require_core(), 1);
2479
- var _TimelineWidget = class extends import_core20.Widget {
2383
+ // src/theme-widget.tsx
2384
+ var import_core16 = __toESM(require_core(), 1);
2385
+ var import_core17 = __toESM(require_core(), 1);
2386
+
2387
+ // src/themes.ts
2388
+ var LightTheme = {
2389
+ "--widget-margin": "12px",
2390
+ "--button-size": "28px",
2391
+ "--button-corner-radius": "8px",
2392
+ "--button-background": "#fff",
2393
+ "--button-stroke": "rgba(255, 255, 255, 0.3)",
2394
+ "--button-inner-stroke": "unset",
2395
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
2396
+ "--button-backdrop-filter": "unset",
2397
+ "--button-icon-idle": "rgba(97, 97, 102, 1)",
2398
+ "--button-icon-hover": "rgba(24, 24, 26, 1)",
2399
+ "--button-text": "rgb(24, 24, 26, 1)",
2400
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
2401
+ "--icon-compass-south-color": "rgb(204, 204, 204)",
2402
+ "--menu-gap": "4px"
2403
+ };
2404
+ var DarkTheme = {
2405
+ "--widget-margin": "12px",
2406
+ "--button-size": "28px",
2407
+ "--button-corner-radius": "8px",
2408
+ "--button-background": "rgba(18, 18, 20, 1)",
2409
+ "--button-stroke": "rgba(18, 18, 20, 0.30)",
2410
+ "--button-inner-stroke": "unset",
2411
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25)",
2412
+ "--button-backdrop-filter": "unset",
2413
+ "--button-icon-idle": "rgba(158, 157, 168, 1)",
2414
+ "--button-icon-hover": "rgba(215, 214, 229, 1)",
2415
+ "--button-text": "rgb(215, 214, 229, 1)",
2416
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
2417
+ "--icon-compass-south-color": "rgb(200, 199, 209)",
2418
+ "--menu-gap": "4px"
2419
+ };
2420
+ var LightGlassTheme = {
2421
+ "--widget-margin": "12px",
2422
+ "--button-size": "28px",
2423
+ "--button-corner-radius": "8px",
2424
+ "--button-background": "rgba(255, 255, 255, 0.6)",
2425
+ "--button-stroke": "rgba(255, 255, 255, 0.3)",
2426
+ "--button-inner-stroke": "1px solid rgba(255, 255, 255, 0.6)",
2427
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
2428
+ "--button-backdrop-filter": "blur(4px)",
2429
+ "--button-icon-idle": "rgba(97, 97, 102, 1)",
2430
+ "--button-icon-hover": "rgba(24, 24, 26, 1)",
2431
+ "--button-text": "rgb(24, 24, 26, 1)",
2432
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
2433
+ "--icon-compass-south-color": "rgb(204, 204, 204)",
2434
+ "--menu-gap": "4px"
2435
+ };
2436
+ var DarkGlassTheme = {
2437
+ "--widget-margin": "12px",
2438
+ "--button-size": "28px",
2439
+ "--button-corner-radius": "8px",
2440
+ "--button-background": "rgba(18, 18, 20, 0.75)",
2441
+ "--button-stroke": "rgba(18, 18, 20, 0.30)",
2442
+ "--button-inner-stroke": "1px solid rgba(18, 18, 20, 0.75)",
2443
+ "--button-shadow": "0px 0px 8px 0px rgba(0, 0, 0, 0.25), 0px 0px 8px 0px rgba(0, 0, 0, 0.1) inset",
2444
+ "--button-backdrop-filter": "blur(4px)",
2445
+ "--button-icon-idle": "rgba(158, 157, 168, 1)",
2446
+ "--button-icon-hover": "rgba(215, 214, 229, 1)",
2447
+ "--button-text": "rgb(215, 214, 229, 1)",
2448
+ "--icon-compass-north-color": "rgb(240, 92, 68)",
2449
+ "--icon-compass-south-color": "rgb(200, 199, 209)",
2450
+ "--menu-gap": "4px"
2451
+ };
2452
+
2453
+ // src/theme-widget.tsx
2454
+ var _ThemeWidget = class extends import_core17.Widget {
2480
2455
  constructor(props = {}) {
2481
- super(props, _TimelineWidget.defaultProps);
2482
- this.id = "timeline";
2483
- this.className = "deck-widget-timeline";
2484
- this.placement = "bottom-left";
2485
- this.playing = false;
2486
- this.timerId = null;
2487
- this.handlePlayPause = () => {
2488
- if (this.playing) {
2489
- this.stop();
2490
- } else {
2491
- this.start();
2492
- }
2493
- };
2494
- this.handleSliderChange = (e3) => {
2495
- const input = e3.target;
2496
- const val = Number(input.value);
2497
- this.currentTime = val;
2498
- this.props.onTimeChange(val);
2456
+ super(props, _ThemeWidget.defaultProps);
2457
+ this.className = "deck-widget-theme";
2458
+ this.placement = "top-left";
2459
+ this.themeMode = "dark";
2460
+ this.themeMode = this._getInitialThemeMode();
2461
+ this.setProps(this.props);
2462
+ }
2463
+ // eslint-disable-next-line complexity
2464
+ setProps(props) {
2465
+ const { lightModeTheme, darkModeTheme } = this.props;
2466
+ this.placement = props.placement ?? this.placement;
2467
+ this.viewId = props.viewId ?? this.viewId;
2468
+ super.setProps(props);
2469
+ switch (this.themeMode) {
2470
+ case "light":
2471
+ if (props.lightModeTheme && !(0, import_core16._deepEqual)(props.lightModeTheme, lightModeTheme, 1)) {
2472
+ this._setThemeMode("light");
2473
+ }
2474
+ break;
2475
+ case "dark":
2476
+ if (props.darkModeTheme && !(0, import_core16._deepEqual)(props.darkModeTheme, darkModeTheme, 1)) {
2477
+ this._setThemeMode("dark");
2478
+ }
2479
+ break;
2480
+ default:
2481
+ import_core16.log.warn(`Invalid theme mode ${this.themeMode}`)();
2482
+ }
2483
+ }
2484
+ onRenderHTML(rootElement) {
2485
+ const { lightModeLabel, darkModeLabel } = this.props;
2486
+ B(
2487
+ /* @__PURE__ */ u2(
2488
+ IconButton,
2489
+ {
2490
+ onClick: this._handleClick.bind(this),
2491
+ label: this.themeMode === "dark" ? darkModeLabel : lightModeLabel,
2492
+ className: this.themeMode === "dark" ? "deck-widget-moon" : "deck-widget-sun"
2493
+ }
2494
+ ),
2495
+ rootElement
2496
+ );
2497
+ }
2498
+ onAdd() {
2499
+ this._setThemeMode(this.themeMode);
2500
+ }
2501
+ _handleClick() {
2502
+ const newThemeMode = this.themeMode === "dark" ? "light" : "dark";
2503
+ this._setThemeMode(newThemeMode);
2504
+ }
2505
+ _setThemeMode(themeMode) {
2506
+ this.themeMode = themeMode;
2507
+ const container = this.rootElement?.closest(".deck-widget-container");
2508
+ if (container) {
2509
+ const themeStyle = themeMode === "dark" ? this.props.darkModeTheme : this.props.lightModeTheme;
2510
+ (0, import_core16._applyStyles)(container, themeStyle);
2511
+ const label = this.themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
2512
+ import_core16.log.log(1, `Switched theme to ${label}`, themeStyle)();
2499
2513
  this.updateHTML();
2500
- };
2501
- this.tick = () => {
2502
- const [min, max] = this.props.timeRange;
2503
- let next = this.currentTime + this.props.step;
2504
- if (next > max) {
2505
- next = min;
2506
- }
2507
- this.currentTime = next;
2508
- this.props.onTimeChange(next);
2514
+ }
2515
+ }
2516
+ /** Read browser preference */
2517
+ _getInitialThemeMode() {
2518
+ const { initialThemeMode } = this.props;
2519
+ return initialThemeMode === "auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : initialThemeMode;
2520
+ }
2521
+ };
2522
+ var ThemeWidget = _ThemeWidget;
2523
+ ThemeWidget.defaultProps = {
2524
+ ...import_core17.Widget.defaultProps,
2525
+ id: "theme",
2526
+ placement: "top-left",
2527
+ viewId: null,
2528
+ lightModeLabel: "Light Mode",
2529
+ lightModeTheme: LightGlassTheme,
2530
+ darkModeLabel: "Dark Mode",
2531
+ darkModeTheme: DarkGlassTheme,
2532
+ initialThemeMode: "auto"
2533
+ };
2534
+
2535
+ // src/loading-widget.tsx
2536
+ var import_core18 = __toESM(require_core(), 1);
2537
+ var _LoadingWidget = class extends import_core18.Widget {
2538
+ constructor(props = {}) {
2539
+ super(props, _LoadingWidget.defaultProps);
2540
+ this.className = "deck-widget-loading";
2541
+ this.placement = "top-left";
2542
+ this.loading = true;
2543
+ this.setProps(this.props);
2544
+ }
2545
+ setProps(props) {
2546
+ this.placement = props.placement ?? this.placement;
2547
+ this.viewId = props.viewId ?? this.viewId;
2548
+ super.setProps(props);
2549
+ }
2550
+ onRenderHTML(rootElement) {
2551
+ B(
2552
+ // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component.
2553
+ this.loading && /* @__PURE__ */ u2(
2554
+ IconButton,
2555
+ {
2556
+ className: "deck-widget-spinner",
2557
+ label: this.props.label,
2558
+ onClick: this.handleClick.bind(this)
2559
+ }
2560
+ ),
2561
+ rootElement
2562
+ );
2563
+ }
2564
+ onRedraw({ layers }) {
2565
+ const loading = layers.some((layer) => !layer.isLoaded);
2566
+ if (loading !== this.loading) {
2567
+ this.loading = loading;
2509
2568
  this.updateHTML();
2510
- if (this.playing) {
2511
- this.timerId = window.setTimeout(this.tick, this.props.playInterval);
2512
- }
2513
- };
2514
- this.currentTime = this.props.initialTime ?? this.props.timeRange[0];
2569
+ }
2570
+ }
2571
+ // TODO(ibgreen) - this should not be a button, see above.
2572
+ handleClick() {
2573
+ }
2574
+ };
2575
+ var LoadingWidget = _LoadingWidget;
2576
+ LoadingWidget.defaultProps = {
2577
+ ...import_core18.Widget.defaultProps,
2578
+ id: "loading",
2579
+ placement: "top-left",
2580
+ viewId: null,
2581
+ label: "Loading layer data"
2582
+ };
2583
+
2584
+ // src/fps-widget.tsx
2585
+ var import_core19 = __toESM(require_core(), 1);
2586
+ var _FpsWidget = class extends import_core19.Widget {
2587
+ constructor(props = {}) {
2588
+ super(props, _FpsWidget.defaultProps);
2589
+ this.className = "deck-widget-fps";
2590
+ this.placement = "top-left";
2591
+ this._lastFps = -1;
2592
+ this.setProps(this.props);
2515
2593
  }
2516
2594
  setProps(props) {
2517
- this.placement = props.placement || this.placement;
2595
+ this.placement = props.placement ?? this.placement;
2596
+ this.viewId = props.viewId ?? this.viewId;
2518
2597
  super.setProps(props);
2519
2598
  }
2520
- onAdd() {
2521
- this.playing = false;
2522
- this.timerId = null;
2523
- }
2524
- onRemove() {
2525
- this.stop();
2599
+ onAdd({}) {
2600
+ this._lastFps = this._getFps();
2601
+ requestAnimationFrame(() => this._animate());
2526
2602
  }
2527
2603
  onRenderHTML(rootElement) {
2604
+ const fps = this._getFps();
2528
2605
  B(
2529
- /* @__PURE__ */ u2("div", { style: { display: "flex", alignItems: "center", pointerEvents: "auto" }, children: [
2530
- /* @__PURE__ */ u2(
2531
- "button",
2532
- {
2533
- type: "button",
2534
- className: "timeline-play-pause",
2535
- title: this.playing ? "Pause" : "Play",
2536
- onClick: this.handlePlayPause,
2537
- children: this.playing ? "\u23F8" : "\u25B6"
2538
- }
2539
- ),
2540
- /* @__PURE__ */ u2(
2541
- "input",
2542
- {
2543
- type: "range",
2544
- className: "timeline-slider",
2545
- min: this.props.timeRange[0],
2546
- max: this.props.timeRange[1],
2547
- step: this.props.step,
2548
- value: this.currentTime,
2549
- onInput: this.handleSliderChange
2550
- }
2551
- )
2552
- ] }),
2606
+ /* @__PURE__ */ u2(IconButton, { children: /* @__PURE__ */ u2("div", { className: "text", children: [
2607
+ "FPS",
2608
+ /* @__PURE__ */ u2("br", {}),
2609
+ fps
2610
+ ] }) }),
2553
2611
  rootElement
2554
2612
  );
2555
2613
  }
2556
- start() {
2557
- this.playing = true;
2558
- this.updateHTML();
2559
- this.tick();
2560
- }
2561
- stop() {
2562
- this.playing = false;
2563
- if (this.timerId !== null) {
2564
- window.clearTimeout(this.timerId);
2565
- this.timerId = null;
2614
+ _animate() {
2615
+ const fps = this._getFps();
2616
+ if (this._lastFps !== fps) {
2617
+ this._lastFps = fps;
2618
+ this.updateHTML();
2566
2619
  }
2567
- this.updateHTML();
2620
+ requestAnimationFrame(() => this._animate());
2621
+ }
2622
+ _getFps() {
2623
+ return Math.round(this.deck?.metrics.fps ?? 0);
2568
2624
  }
2569
2625
  };
2570
- var TimelineWidget = _TimelineWidget;
2571
- TimelineWidget.defaultProps = {
2572
- ...import_core20.Widget.defaultProps,
2573
- id: "timeline",
2574
- placement: "bottom-left",
2575
- timeRange: [0, 100],
2576
- step: 1,
2577
- initialTime: void 0,
2578
- onTimeChange: () => {
2579
- },
2580
- playInterval: 1e3
2626
+ var FpsWidget = _FpsWidget;
2627
+ FpsWidget.defaultProps = {
2628
+ ...import_core19.Widget.defaultProps,
2629
+ id: "fps",
2630
+ placement: "top-left",
2631
+ viewId: null
2581
2632
  };
2582
2633
 
2583
- // src/view-selector-widget.tsx
2584
- var import_core21 = __toESM(require_core(), 1);
2585
-
2586
- // src/lib/components/icon-menu.tsx
2587
- var MENU_STYLE2 = {
2588
- position: "absolute",
2589
- top: "100%",
2590
- left: 0,
2591
- background: "white",
2592
- border: "1px solid #ccc",
2593
- borderRadius: "4px",
2594
- marginTop: "4px",
2595
- zIndex: 100
2596
- };
2597
- var MENU_ITEM_STYLE2 = {
2598
- background: "white",
2599
- border: "none",
2600
- padding: "4px",
2601
- cursor: "pointer",
2602
- pointerEvents: "auto"
2603
- };
2604
- function IconMenu(props) {
2605
- const [menuOpen, setMenuOpen] = h2(false);
2606
- const containerRef = A2(null);
2607
- const handleClickOutside = (event) => {
2608
- if (containerRef.current && !containerRef.current.contains(event.target)) {
2609
- setMenuOpen(false);
2610
- }
2611
- };
2612
- y2(() => {
2613
- document.addEventListener("mousedown", handleClickOutside);
2614
- return () => {
2615
- document.removeEventListener("mousedown", handleClickOutside);
2616
- };
2617
- }, [containerRef]);
2618
- const [selectedItem, setSelectedItem] = h2(props.initialItem);
2619
- const handleSelectItem = (item) => {
2620
- setSelectedItem(item);
2621
- setMenuOpen(false);
2622
- props.onItemSelected(item);
2623
- };
2624
- const handleButtonClick = () => setMenuOpen(!menuOpen);
2625
- const selectedMenuItem = props.menuItems.find((item) => item.value === selectedItem);
2626
- const label = props.label || selectedMenuItem?.label || "";
2627
- const Icon = props.icon || selectedMenuItem?.icon;
2628
- return /* @__PURE__ */ u2("div", { style: { position: "relative", display: "inline-block" }, ref: containerRef, children: [
2629
- /* @__PURE__ */ u2(IconButton, { className: props.className, label, onClick: handleButtonClick, children: /* @__PURE__ */ u2(Icon, {}) }),
2630
- menuOpen && /* @__PURE__ */ u2("div", { style: MENU_STYLE2, children: /* @__PURE__ */ u2("div", { style: { display: "flex", flexDirection: "column" }, children: props.menuItems.map((item) => /* @__PURE__ */ u2(
2631
- "button",
2632
- {
2633
- style: MENU_ITEM_STYLE2,
2634
- title: item.label,
2635
- onClick: () => handleSelectItem(item.value),
2636
- children: item.icon
2637
- },
2638
- item.value
2639
- )) }) })
2640
- ] });
2634
+ // src/stats-widget.tsx
2635
+ var import_core20 = __toESM(require_core(), 1);
2636
+ var import_core21 = __toESM(require_core2(), 1);
2637
+ var RIGHT_ARROW = "\u25B6";
2638
+ var DOWN_ARROW = "\u2B07";
2639
+ var DEFAULT_COUNT_FORMATTER = (stat) => `${stat.name}: ${stat.count}`;
2640
+ function formatTime(time) {
2641
+ return time < 1e3 ? `${time.toFixed(2)}ms` : `${(time / 1e3).toFixed(2)}s`;
2641
2642
  }
2642
-
2643
- // src/view-selector-widget.tsx
2644
- var _ViewSelectorWidget = class extends import_core21.Widget {
2643
+ function formatMemory(bytes) {
2644
+ const mb = bytes / 1e6;
2645
+ return `${mb.toFixed(1)} MB`;
2646
+ }
2647
+ var DEFAULT_FORMATTERS = {
2648
+ count: DEFAULT_COUNT_FORMATTER,
2649
+ averageTime: (stat) => `${stat.name}: ${formatTime(stat.getAverageTime())}`,
2650
+ totalTime: (stat) => `${stat.name}: ${formatTime(stat.time)}`,
2651
+ fps: (stat) => `${stat.name}: ${Math.round(stat.getHz())}fps`,
2652
+ memory: (stat) => `${stat.name}: ${formatMemory(stat.count)}`
2653
+ };
2654
+ var _StatsWidget = class extends import_core20.Widget {
2645
2655
  constructor(props = {}) {
2646
- super(props, _ViewSelectorWidget.defaultProps);
2647
- this.className = "deck-widget-view-selector";
2656
+ super(props, _StatsWidget.defaultProps);
2657
+ this.className = "deck-widget-stats";
2648
2658
  this.placement = "top-left";
2649
- this.handleSelectMode = (viewMode) => {
2650
- this.viewMode = viewMode;
2659
+ this._counter = 0;
2660
+ this.collapsed = true;
2661
+ this._toggleCollapsed = () => {
2662
+ this.collapsed = !this.collapsed;
2651
2663
  this.updateHTML();
2652
2664
  };
2653
- this.placement = this.props.placement;
2654
- this.viewMode = this.props.initialViewMode;
2665
+ this._formatters = { ...DEFAULT_FORMATTERS };
2666
+ this._resetOnUpdate = { ...this.props.resetOnUpdate };
2667
+ this._stats = this.props.stats;
2668
+ this.setProps(props);
2655
2669
  }
2656
2670
  setProps(props) {
2657
- super.setProps(props);
2658
2671
  this.placement = props.placement ?? this.placement;
2672
+ this.viewId = props.viewId ?? this.viewId;
2673
+ this._stats = this._getStats();
2674
+ if (props.formatters) {
2675
+ for (const name in props.formatters) {
2676
+ const f4 = props.formatters[name];
2677
+ this._formatters[name] = typeof f4 === "string" ? DEFAULT_FORMATTERS[f4] || DEFAULT_COUNT_FORMATTER : f4;
2678
+ }
2679
+ }
2680
+ if (props.resetOnUpdate) {
2681
+ this._resetOnUpdate = { ...props.resetOnUpdate };
2682
+ }
2683
+ super.setProps(props);
2684
+ }
2685
+ onAdd() {
2686
+ this._stats = this._getStats();
2687
+ this.updateHTML();
2659
2688
  }
2660
2689
  onRenderHTML(rootElement) {
2661
- B(
2662
- /* @__PURE__ */ u2(
2663
- IconMenu,
2664
- {
2665
- className: "deck-widget-view-selector",
2666
- menuItems: MENU_ITEMS,
2667
- initialItem: this.props.initialViewMode,
2668
- onItemSelected: this.handleSelectMode
2690
+ const stats = this._stats;
2691
+ const collapsed = this.collapsed;
2692
+ const title = this.props.title || stats?.id || "Stats";
2693
+ const items = [];
2694
+ if (!collapsed && stats) {
2695
+ stats.forEach((stat) => {
2696
+ const lines = this._getLines(stat);
2697
+ if (this._resetOnUpdate && this._resetOnUpdate[stat.name]) {
2698
+ stat.reset();
2669
2699
  }
2670
- ),
2700
+ lines.forEach((line, i4) => {
2701
+ items.push(
2702
+ /* @__PURE__ */ u2("div", { style: { whiteSpace: "pre" }, children: line }, `${stat.name}-${i4}`)
2703
+ );
2704
+ });
2705
+ });
2706
+ }
2707
+ B(
2708
+ /* @__PURE__ */ u2("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [
2709
+ /* @__PURE__ */ u2(
2710
+ "div",
2711
+ {
2712
+ className: "deck-widget-stats-header",
2713
+ style: { cursor: "pointer", pointerEvents: "auto" },
2714
+ onClick: this._toggleCollapsed,
2715
+ children: [
2716
+ collapsed ? RIGHT_ARROW : DOWN_ARROW,
2717
+ " ",
2718
+ title
2719
+ ]
2720
+ }
2721
+ ),
2722
+ !collapsed && /* @__PURE__ */ u2("div", { className: "deck-widget-stats-content", children: items })
2723
+ ] }),
2671
2724
  rootElement
2672
2725
  );
2673
2726
  }
2674
- };
2675
- var ViewSelectorWidget = _ViewSelectorWidget;
2676
- ViewSelectorWidget.defaultProps = {
2677
- ...import_core21.Widget.defaultProps,
2678
- id: "view-selector",
2679
- placement: "top-left",
2680
- label: "Split View",
2681
- initialViewMode: "single",
2682
- onViewModeChange: (viewMode) => {
2683
- console.log(viewMode);
2727
+ onRedraw() {
2728
+ const framesPerUpdate = Math.max(1, this.props.framesPerUpdate || 1);
2729
+ if (this._counter++ % framesPerUpdate === 0) {
2730
+ this._stats = this._getStats();
2731
+ this.updateHTML();
2732
+ }
2733
+ }
2734
+ _getStats() {
2735
+ switch (this.props.type) {
2736
+ case "deck":
2737
+ return this.deck?.stats;
2738
+ case "luma":
2739
+ return Array.from(import_core21.luma.stats.stats.values())[0];
2740
+ case "device":
2741
+ const device = this.deck?.device;
2742
+ const stats = device?.statsManager.stats.values();
2743
+ return stats ? Array.from(stats)[0] : void 0;
2744
+ case "custom":
2745
+ return this.props.stats;
2746
+ default:
2747
+ throw new Error(`Unknown stats type: ${this.props.type}`);
2748
+ }
2749
+ }
2750
+ _getLines(stat) {
2751
+ const formatter = this._formatters[stat.name] || this._formatters[stat.type || ""] || DEFAULT_COUNT_FORMATTER;
2752
+ return formatter(stat).split("\n");
2684
2753
  }
2685
2754
  };
2686
- var ICON_STYLE = { width: "24px", height: "24px" };
2687
- var ICONS = {
2688
- single: () => /* @__PURE__ */ u2("svg", { width: "24", height: "24", style: ICON_STYLE, children: /* @__PURE__ */ u2("rect", { x: "4", y: "4", width: "16", height: "16", stroke: "black", fill: "none", strokeWidth: "2" }) }),
2689
- "split-horizontal": () => /* @__PURE__ */ u2("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
2690
- /* @__PURE__ */ u2("rect", { x: "4", y: "4", width: "16", height: "7", stroke: "black", fill: "none", strokeWidth: "2" }),
2691
- /* @__PURE__ */ u2("rect", { x: "4", y: "13", width: "16", height: "7", stroke: "black", fill: "none", strokeWidth: "2" })
2692
- ] }),
2693
- "split-vertical": () => /* @__PURE__ */ u2("svg", { width: "24", height: "24", style: ICON_STYLE, children: [
2694
- /* @__PURE__ */ u2("rect", { x: "4", y: "4", width: "7", height: "16", stroke: "black", fill: "none", strokeWidth: "2" }),
2695
- /* @__PURE__ */ u2("rect", { x: "13", y: "4", width: "7", height: "16", stroke: "black", fill: "none", strokeWidth: "2" })
2696
- ] })
2755
+ var StatsWidget = _StatsWidget;
2756
+ StatsWidget.defaultProps = {
2757
+ ...import_core20.Widget.defaultProps,
2758
+ type: "deck",
2759
+ placement: "top-left",
2760
+ viewId: null,
2761
+ stats: void 0,
2762
+ title: "Stats",
2763
+ framesPerUpdate: 1,
2764
+ formatters: {},
2765
+ resetOnUpdate: {},
2766
+ id: "stats"
2697
2767
  };
2698
- var MENU_ITEMS = [
2699
- { value: "single", icon: ICONS.single, label: "Single View" },
2700
- { value: "split-horizontal", icon: ICONS["split-horizontal"], label: "Split Horizontal" },
2701
- { value: "split-vertical", icon: ICONS["split-vertical"], label: "Split Vertical" }
2702
- ];
2703
2768
  return __toCommonJS(bundle_exports);
2704
2769
  })();
2705
2770
  return __exports__;