@deck.gl/widgets 9.2.0-beta.1 → 9.2.0-beta.3

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 +5 -1
  8. package/dist/context-menu-widget.js.map +1 -1
  9. package/dist/dist.dev.js +968 -902
  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 +13 -14
  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 +5 -6
  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 +687 -670
  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 +15 -14
  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 +8 -3
  103. package/src/fps-widget.tsx +25 -16
  104. package/src/fullscreen-widget.tsx +9 -3
  105. package/src/geocoder-widget.tsx +9 -9
  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 +59 -16
  125. package/src/zoom-widget.tsx +8 -6
@@ -2,15 +2,17 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {WidgetPlacement, Layer} from '@deck.gl/core';
5
+ import type {WidgetPlacement, Layer, WidgetProps} from '@deck.gl/core';
6
6
  import {render} from 'preact';
7
- import {Widget, WidgetProps} from '@deck.gl/core';
7
+ import {Widget} from '@deck.gl/core';
8
8
  import {IconButton} from './lib/components/icon-button';
9
9
 
10
10
  /** Properties for the LoadingWidget */
11
11
  export type LoadingWidgetProps = WidgetProps & {
12
12
  /** Widget positioning within the view. Default 'top-left'. */
13
13
  placement?: WidgetPlacement;
14
+ /** View to attach to and interact with. Required when using multiple views */
15
+ viewId?: string | null;
14
16
  /** Tooltip message when loading */
15
17
  label?: string;
16
18
  };
@@ -23,6 +25,7 @@ export class LoadingWidget extends Widget<LoadingWidgetProps> {
23
25
  ...Widget.defaultProps,
24
26
  id: 'loading',
25
27
  placement: 'top-left',
28
+ viewId: null,
26
29
  label: 'Loading layer data'
27
30
  };
28
31
 
@@ -32,11 +35,12 @@ export class LoadingWidget extends Widget<LoadingWidgetProps> {
32
35
 
33
36
  constructor(props: LoadingWidgetProps = {}) {
34
37
  super(props, LoadingWidget.defaultProps);
35
- this.placement = props.placement ?? this.placement;
38
+ this.setProps(this.props);
36
39
  }
37
40
 
38
41
  setProps(props: Partial<LoadingWidgetProps>) {
39
42
  this.placement = props.placement ?? this.placement;
43
+ this.viewId = props.viewId ?? this.viewId;
40
44
  super.setProps(props);
41
45
  }
42
46
 
@@ -45,7 +49,7 @@ export class LoadingWidget extends Widget<LoadingWidgetProps> {
45
49
  // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component.
46
50
  this.loading && (
47
51
  <IconButton
48
- className="deck-widget-spinner-icon"
52
+ className="deck-widget-spinner"
49
53
  label={this.props.label}
50
54
  onClick={this.handleClick.bind(this)}
51
55
  />
@@ -55,7 +59,7 @@ export class LoadingWidget extends Widget<LoadingWidgetProps> {
55
59
  }
56
60
 
57
61
  onRedraw({layers}: {layers: Layer[]}): void {
58
- const loading = !layers.some(layer => !layer.isLoaded);
62
+ const loading = layers.some(layer => !layer.isLoaded);
59
63
  if (loading !== this.loading) {
60
64
  this.loading = loading;
61
65
  this.updateHTML();
@@ -2,22 +2,20 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {WidgetPlacement} from '@deck.gl/core';
5
+ import type {WidgetPlacement, WidgetProps} from '@deck.gl/core';
6
+ import type {ViewStateMap, ViewOrViews} from '@deck.gl/core/src/lib/view-manager';
6
7
  import {render} from 'preact';
7
- import {Widget, WidgetProps} from '@deck.gl/core';
8
+ import {Widget} from '@deck.gl/core';
8
9
  import {IconButton} from './lib/components/icon-button';
9
10
 
10
- /** @todo - is the the best we can do? */
11
- type ViewState = Record<string, unknown>;
12
-
13
11
  /** Properties for the ResetViewWidget */
14
- export type ResetViewWidgetProps = WidgetProps & {
12
+ export type ResetViewWidgetProps<ViewsT extends ViewOrViews = null> = WidgetProps & {
15
13
  /** Widget positioning within the view. Default 'top-left'. */
16
14
  placement?: WidgetPlacement;
17
15
  /** Tooltip message */
18
16
  label?: string;
19
17
  /** The initial view state to reset the view to. Defaults to deck.props.initialViewState */
20
- initialViewState?: ViewState;
18
+ initialViewState?: ViewStateMap<ViewsT>;
21
19
  /** View to interact with. Required when using multiple views. */
22
20
  viewId?: string | null;
23
21
  };
@@ -25,26 +23,30 @@ export type ResetViewWidgetProps = WidgetProps & {
25
23
  /**
26
24
  * A button widget that resets the view state of deck to an initial state.
27
25
  */
28
- export class ResetViewWidget extends Widget<ResetViewWidgetProps> {
26
+ export class ResetViewWidget<ViewsT extends ViewOrViews = null> extends Widget<
27
+ ResetViewWidgetProps<ViewsT>,
28
+ ViewsT
29
+ > {
29
30
  static defaultProps: Required<ResetViewWidgetProps> = {
30
31
  ...Widget.defaultProps,
31
32
  id: 'reset-view',
32
33
  placement: 'top-left',
33
34
  label: 'Reset View',
34
35
  initialViewState: undefined!,
35
- viewId: undefined!
36
+ viewId: null
36
37
  };
37
38
 
38
39
  className = 'deck-widget-reset-view';
39
40
  placement: WidgetPlacement = 'top-left';
40
41
 
41
- constructor(props: ResetViewWidgetProps = {}) {
42
- super(props, ResetViewWidget.defaultProps);
43
- this.placement = props.placement ?? this.placement;
42
+ constructor(props: ResetViewWidgetProps<ViewsT> = {}) {
43
+ super(props, ResetViewWidget.defaultProps as Required<ResetViewWidgetProps<ViewsT>>);
44
+ this.setProps(this.props);
44
45
  }
45
46
 
46
- setProps(props: Partial<ResetViewWidgetProps>) {
47
+ setProps(props: Partial<ResetViewWidgetProps<ViewsT>>) {
47
48
  this.placement = props.placement ?? this.placement;
49
+ this.viewId = props.viewId ?? this.viewId;
48
50
  super.setProps(props);
49
51
  }
50
52
 
@@ -64,10 +66,10 @@ export class ResetViewWidget extends Widget<ResetViewWidgetProps> {
64
66
  this.setViewState(initialViewState);
65
67
  }
66
68
 
67
- setViewState(viewState: ViewState) {
68
- const viewId = this.props.viewId || viewState?.id || 'default-view';
69
+ setViewState(viewState?: ViewStateMap<ViewsT>) {
70
+ const viewId = (this.props.viewId || 'default-view') as unknown as string;
69
71
  const nextViewState = {
70
- ...viewState
72
+ ...(viewId !== 'default-view' ? viewState?.[viewId] : viewState)
71
73
  // only works for geospatial?
72
74
  // transitionDuration: this.props.transitionDuration,
73
75
  // transitionInterpolator: new FlyToInterpolator()
@@ -2,14 +2,17 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import type {WidgetPlacement, Viewport} from '@deck.gl/core';
5
+ import type {WidgetPlacement, Viewport, WidgetProps} from '@deck.gl/core';
6
6
  import {render} from 'preact';
7
- import {Widget, WidgetProps} from '@deck.gl/core';
7
+ import {Widget} from '@deck.gl/core';
8
8
 
9
9
  export type ScaleWidgetProps = WidgetProps & {
10
+ /** Widget positioning within the view. Default 'bottom-left'. */
10
11
  placement?: WidgetPlacement;
12
+ /** Label for the scale widget */
11
13
  label?: string;
12
- onCapture?: (widget: ScaleWidget) => void;
14
+ /** View to attach to and interact with. Required when using multiple views */
15
+ viewId?: string | null;
13
16
  };
14
17
 
15
18
  /**
@@ -25,7 +28,7 @@ export class ScaleWidget extends Widget<ScaleWidgetProps> {
25
28
  id: 'scale',
26
29
  placement: 'bottom-left',
27
30
  label: 'Scale',
28
- onCapture: undefined!
31
+ viewId: null
29
32
  };
30
33
 
31
34
  className = 'deck-widget-scale';
@@ -40,11 +43,12 @@ export class ScaleWidget extends Widget<ScaleWidgetProps> {
40
43
 
41
44
  constructor(props: ScaleWidgetProps = {}) {
42
45
  super(props, ScaleWidget.defaultProps);
43
- this.placement = props.placement ?? this.placement;
46
+ this.setProps(this.props);
44
47
  }
45
48
 
46
49
  setProps(props: Partial<ScaleWidgetProps>): void {
47
50
  this.placement = props.placement ?? this.placement;
51
+ this.viewId = props.viewId ?? this.viewId;
48
52
  super.setProps(props);
49
53
  }
50
54
 
@@ -3,15 +3,17 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  /* global document */
6
- import type {WidgetPlacement} from '@deck.gl/core';
6
+ import type {WidgetPlacement, WidgetProps} from '@deck.gl/core';
7
7
  import {render} from 'preact';
8
- import {Widget, WidgetProps} from '@deck.gl/core';
8
+ import {Widget} from '@deck.gl/core';
9
9
  import {IconButton} from './lib/components/icon-button';
10
10
 
11
11
  /** Properties for the ScreenshotWidget */
12
12
  export type ScreenshotWidgetProps = WidgetProps & {
13
13
  /** Widget positioning within the view. Default 'top-left'. */
14
14
  placement?: WidgetPlacement;
15
+ /** View to attach to and interact with. Required when using multiple views. */
16
+ viewId?: string | null;
15
17
  /** Tooltip message */
16
18
  label?: string;
17
19
  /** Filename to save to */
@@ -31,6 +33,7 @@ export class ScreenshotWidget extends Widget<ScreenshotWidgetProps> {
31
33
  ...Widget.defaultProps,
32
34
  id: 'screenshot',
33
35
  placement: 'top-left',
36
+ viewId: null,
34
37
  label: 'Screenshot',
35
38
  filename: 'screenshot.png',
36
39
  imageFormat: 'image/png',
@@ -42,11 +45,12 @@ export class ScreenshotWidget extends Widget<ScreenshotWidgetProps> {
42
45
 
43
46
  constructor(props: ScreenshotWidgetProps = {}) {
44
47
  super(props, ScreenshotWidget.defaultProps);
45
- this.placement = props.placement ?? this.placement;
48
+ this.setProps(this.props);
46
49
  }
47
50
 
48
51
  setProps(props: Partial<ScreenshotWidgetProps>) {
49
52
  this.placement = props.placement ?? this.placement;
53
+ this.viewId = props.viewId ?? this.viewId;
50
54
  super.setProps(props);
51
55
  }
52
56
 
@@ -1,11 +1,10 @@
1
- // SplitterWidget.tsx
2
1
  // deck.gl
3
2
  // SPDX-License-Identifier: MIT
4
3
  // Copyright (c) vis.gl contributors
5
4
 
6
5
  import {h, render} from 'preact';
7
6
  import {useState, useRef} from 'preact/hooks';
8
- import {Widget, WidgetProps} from '@deck.gl/core';
7
+ import {Widget, type WidgetProps} from '@deck.gl/core';
9
8
 
10
9
  /** Properties for the SplitterWidget */
11
10
  export type SplitterWidgetProps = WidgetProps & {
@@ -47,7 +46,6 @@ export class SplitterWidget extends Widget<SplitterWidgetProps> {
47
46
  placement = 'fill' as const;
48
47
 
49
48
  constructor(props: SplitterWidgetProps) {
50
- // No placement prop is used.
51
49
  super(props, SplitterWidget.defaultProps);
52
50
  }
53
51
 
@@ -1,4 +1,8 @@
1
- import {Widget, WidgetPlacement, WidgetProps} from '@deck.gl/core';
1
+ // deck.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {Widget, type WidgetPlacement, type WidgetProps} from '@deck.gl/core';
2
6
  import {luma} from '@luma.gl/core';
3
7
  import {render} from 'preact';
4
8
  import type {Stats, Stat} from '@probe.gl/stats';
@@ -26,6 +30,11 @@ export const DEFAULT_FORMATTERS: Record<string, (stat: Stat) => string> = {
26
30
  };
27
31
 
28
32
  export type StatsWidgetProps = WidgetProps & {
33
+ /** Widget positioning within the view. Default 'top-left'. */
34
+ placement?: WidgetPlacement;
35
+ /** View to attach to and interact with. Required when using multiple views. */
36
+ viewId?: string | null;
37
+ /** Type of stats to display. */
29
38
  type?: 'deck' | 'luma' | 'device' | 'custom';
30
39
  /** Stats object to visualize. */
31
40
  stats?: Stats;
@@ -44,6 +53,8 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
44
53
  static defaultProps: Required<StatsWidgetProps> = {
45
54
  ...Widget.defaultProps,
46
55
  type: 'deck',
56
+ placement: 'top-left',
57
+ viewId: null,
47
58
  stats: undefined!,
48
59
  title: 'Stats',
49
60
  framesPerUpdate: 1,
@@ -64,13 +75,14 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
64
75
  constructor(props: StatsWidgetProps = {}) {
65
76
  super(props, StatsWidget.defaultProps);
66
77
  this._formatters = {...DEFAULT_FORMATTERS};
67
- this.setProps(props);
68
78
  this._resetOnUpdate = {...this.props.resetOnUpdate};
69
79
  this._stats = this.props.stats;
80
+ this.setProps(props);
70
81
  }
71
82
 
72
83
  setProps(props: Partial<StatsWidgetProps>): void {
73
- super.setProps(props);
84
+ this.placement = props.placement ?? this.placement;
85
+ this.viewId = props.viewId ?? this.viewId;
74
86
  this._stats = this._getStats();
75
87
  if (props.formatters) {
76
88
  for (const name in props.formatters) {
@@ -82,6 +94,7 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
82
94
  if (props.resetOnUpdate) {
83
95
  this._resetOnUpdate = {...props.resetOnUpdate};
84
96
  }
97
+ super.setProps(props);
85
98
  }
86
99
 
87
100
  onAdd(): void {
@@ -120,7 +133,7 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
120
133
  >
121
134
  {collapsed ? RIGHT_ARROW : DOWN_ARROW} {title}
122
135
  </div>
123
- {!collapsed && <div className="deck-widget-stats-content deck-widget-common">{items}</div>}
136
+ {!collapsed && <div className="deck-widget-stats-content">{items}</div>}
124
137
  </div>,
125
138
  rootElement
126
139
  );
@@ -129,7 +142,7 @@ export class StatsWidget extends Widget<StatsWidgetProps> {
129
142
  onRedraw(): void {
130
143
  const framesPerUpdate = Math.max(1, this.props.framesPerUpdate || 1);
131
144
  if (this._counter++ % framesPerUpdate === 0) {
132
- console.log('Redrawing stats widget');
145
+ this._stats = this._getStats();
133
146
  this.updateHTML();
134
147
  }
135
148
  }
@@ -93,6 +93,15 @@
93
93
  background-color: var(--button-icon-hover, rgb(24, 24, 26));
94
94
  }
95
95
 
96
+ /* Icon menu styles */
97
+ .deck-widget .deck-widget-icon-menu {
98
+ position: absolute;
99
+ top: 100%;
100
+ left: 0;
101
+ margin-top: var(--menu-gap, 4px);
102
+ z-index: 100;
103
+ }
104
+
96
105
  /* Fullscreen styles */
97
106
  .deck-widget.deck-widget-fullscreen button.deck-widget-fullscreen-enter .deck-widget-icon {
98
107
  mask-image: var(
@@ -116,16 +125,6 @@
116
125
  );
117
126
  }
118
127
 
119
- @keyframes spin {
120
- to {
121
- transform: rotate(360deg);
122
- }
123
- }
124
-
125
- .deck-widget.deck-widget-loading button.deck-widget-spinner-icon .deck-widget-icon {
126
- animation: spin 1s linear infinite;
127
- }
128
-
129
128
  .deck-pseudo-fullscreen {
130
129
  height: 100% !important;
131
130
  left: 0 !important;
@@ -215,21 +214,39 @@
215
214
  cursor: pointer;
216
215
  }
217
216
 
218
- .deck-widget.deck-widget-timeline button.timeline-play-pause {
219
- background: var(--button-background, #fff);
220
- border: var(--button-inner-stroke, unset);
221
- border-radius: var(--button-corner-radius, 4px);
222
- width: var(--button-size, 28px);
223
- height: var(--button-size, 28px);
224
- cursor: pointer;
225
- display: flex;
226
- align-items: center;
227
- justify-content: center;
228
- padding: 0;
229
- pointer-events: 'auto';
217
+ .deck-widget.deck-widget-timeline .text {
218
+ color: var(--button-icon-idle, rgb(97, 97, 102));
219
+ }
220
+
221
+ .deck-widget.deck-widget-timeline .text:hover {
222
+ color: var(--button-icon-hover, rgb(24, 24, 26));
230
223
  }
231
224
 
232
- .deck-widget-spinner-icon {
225
+ /* FPS widget styles */
226
+
227
+ .deck-widget.deck-widget-fps button {
228
+ pointer-events: none;
229
+ }
230
+
231
+ .deck-widget.deck-widget-fps .text {
232
+ color: var(--button-text, rgb(24, 24, 26));
233
+ font-family: monospace;
234
+ font-size: 8px;
235
+ font-weight: 700;
236
+ }
237
+
238
+ /* Loading styles */
239
+ @keyframes spin {
240
+ to {
241
+ transform: rotate(360deg);
242
+ }
243
+ }
244
+
245
+ .deck-widget.deck-widget-loading button.deck-widget-spinner .deck-widget-icon {
246
+ animation: spin 1s linear infinite;
247
+ }
248
+
249
+ .deck-widget.deck-widget-loading button.deck-widget-spinner .deck-widget-icon {
233
250
  mask-image: var(
234
251
  --loading-icon,
235
252
  url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" ><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>')
@@ -240,21 +257,28 @@
240
257
  );
241
258
  }
242
259
 
260
+ /* Scale styles */
243
261
  .deck-widget .deck-widget-scale {
244
262
  background-color: var(--button-icon-idle, #616166);
245
263
  background-position: 50%;
246
264
  display: block;
247
265
  }
248
266
 
249
- .deck-widget .deck-widget-geolocate {
267
+ /* Geocoder styles */
268
+ .deck-widget .deck-widget-geocoder {
250
269
  pointer-events: auto;
251
270
  cursor: pointer;
252
271
  }
253
272
 
254
- /**
255
- * Reusable styles for widgets
256
- */
257
- .deck-widget-common {
273
+ /* Stats styles */
274
+ .deck-widget-stats-container {
275
+ border-radius: var(--button-corner-radius, 4px);
276
+ display: inline-flex;
277
+ flex-direction: column;
278
+ padding: 4px;
279
+ font-size: 0.7rem;
280
+ /* Approximately 14px */
281
+ line-height: 1.6;
258
282
  font-family:
259
283
  'SF Mono',
260
284
  /* macOS */
@@ -268,11 +292,5 @@
268
292
  /* Linux */
269
293
  monospace;
270
294
  background-color: var(--button-background, #fff);
271
- color: #333;
272
- }
273
-
274
- .deck-widget-stats-container {
275
- font-size: 0.7rem;
276
- /* Approximately 14px */
277
- line-height: 1.6;
295
+ color: var(--button-text, rgb(24, 24, 26));
278
296
  }
@@ -3,7 +3,7 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import {log, _deepEqual as deepEqual, _applyStyles as applyStyles} from '@deck.gl/core';
6
- import {Widget, WidgetProps, WidgetPlacement} from '@deck.gl/core';
6
+ import {Widget, type WidgetProps, type WidgetPlacement} from '@deck.gl/core';
7
7
  import {render} from 'preact';
8
8
  // import {useCallback} from 'preact/hooks';
9
9
  import {IconButton} from './lib/components/icon-button';
@@ -13,6 +13,8 @@ import {LightGlassTheme, DarkGlassTheme} from './themes';
13
13
  export type ThemeWidgetProps = WidgetProps & {
14
14
  /** Widget positioning within the view. Default 'top-left'. */
15
15
  placement?: WidgetPlacement;
16
+ /** View to attach to and interact with. Required when using multiple views. */
17
+ viewId?: string | null;
16
18
  /** Tooltip message when dark mode is selected. */
17
19
  lightModeLabel?: string;
18
20
  /** Styles for light mode theme */
@@ -30,6 +32,7 @@ export class ThemeWidget extends Widget<ThemeWidgetProps> {
30
32
  ...Widget.defaultProps,
31
33
  id: 'theme',
32
34
  placement: 'top-left',
35
+ viewId: null,
33
36
  lightModeLabel: 'Light Mode',
34
37
  lightModeTheme: LightGlassTheme,
35
38
  darkModeLabel: 'Dark Mode',
@@ -43,17 +46,17 @@ export class ThemeWidget extends Widget<ThemeWidgetProps> {
43
46
 
44
47
  constructor(props: ThemeWidgetProps = {}) {
45
48
  super(props, ThemeWidget.defaultProps);
46
- this.placement = props.placement ?? this.placement;
47
49
  this.themeMode = this._getInitialThemeMode();
50
+ this.setProps(this.props);
48
51
  }
49
52
 
50
53
  // eslint-disable-next-line complexity
51
54
  setProps(props: Partial<ThemeWidgetProps>) {
52
55
  const {lightModeTheme, darkModeTheme} = this.props;
53
56
  this.placement = props.placement ?? this.placement;
57
+ this.viewId = props.viewId ?? this.viewId;
54
58
  super.setProps(props);
55
59
 
56
- // Update if current theme definition changed
57
60
  switch (this.themeMode) {
58
61
  case 'light':
59
62
  if (props.lightModeTheme && !deepEqual(props.lightModeTheme, lightModeTheme, 1)) {
@@ -65,6 +68,8 @@ export class ThemeWidget extends Widget<ThemeWidgetProps> {
65
68
  this._setThemeMode('dark');
66
69
  }
67
70
  break;
71
+ default:
72
+ log.warn(`Invalid theme mode ${this.themeMode}`)();
68
73
  }
69
74
  }
70
75
 
@@ -102,7 +107,7 @@ export class ThemeWidget extends Widget<ThemeWidgetProps> {
102
107
 
103
108
  const label =
104
109
  this.themeMode === 'dark' ? this.props.darkModeLabel : this.props.lightModeLabel;
105
- log.log(1, `Switched theme to ${label}`, themeStyle);
110
+ log.log(1, `Switched theme to ${label}`, themeStyle)();
106
111
 
107
112
  this.updateHTML();
108
113
  }
package/src/themes.ts CHANGED
@@ -18,9 +18,12 @@ export type DeckWidgetTheme = {
18
18
  '--button-backdrop-filter'?: string;
19
19
  '--button-icon-idle'?: string;
20
20
  '--button-icon-hover'?: string;
21
+ '--button-text'?: string;
21
22
  // inter-icon color
22
23
  '--icon-compass-north-color'?: string;
23
24
  '--icon-compass-south-color'?: string;
25
+ // menu
26
+ '--menu-gap'?: string;
24
27
  };
25
28
 
26
29
  export const LightTheme = {
@@ -34,8 +37,10 @@ export const LightTheme = {
34
37
  '--button-backdrop-filter': 'unset',
35
38
  '--button-icon-idle': 'rgba(97, 97, 102, 1)',
36
39
  '--button-icon-hover': 'rgba(24, 24, 26, 1)',
40
+ '--button-text': 'rgb(24, 24, 26, 1)',
37
41
  '--icon-compass-north-color': 'rgb(240, 92, 68)',
38
- '--icon-compass-south-color': 'rgb(204, 204, 204)'
42
+ '--icon-compass-south-color': 'rgb(204, 204, 204)',
43
+ '--menu-gap': '4px'
39
44
  } as const satisfies Required<DeckWidgetTheme>;
40
45
 
41
46
  export const DarkTheme = {
@@ -49,8 +54,10 @@ export const DarkTheme = {
49
54
  '--button-backdrop-filter': 'unset',
50
55
  '--button-icon-idle': 'rgba(158, 157, 168, 1)',
51
56
  '--button-icon-hover': 'rgba(215, 214, 229, 1)',
57
+ '--button-text': 'rgb(215, 214, 229, 1)',
52
58
  '--icon-compass-north-color': 'rgb(240, 92, 68)',
53
- '--icon-compass-south-color': 'rgb(200, 199, 209)'
59
+ '--icon-compass-south-color': 'rgb(200, 199, 209)',
60
+ '--menu-gap': '4px'
54
61
  } as const satisfies Required<DeckWidgetTheme>;
55
62
 
56
63
  export const LightGlassTheme = {
@@ -65,8 +72,10 @@ export const LightGlassTheme = {
65
72
  '--button-backdrop-filter': 'blur(4px)',
66
73
  '--button-icon-idle': 'rgba(97, 97, 102, 1)',
67
74
  '--button-icon-hover': 'rgba(24, 24, 26, 1)',
75
+ '--button-text': 'rgb(24, 24, 26, 1)',
68
76
  '--icon-compass-north-color': 'rgb(240, 92, 68)',
69
- '--icon-compass-south-color': 'rgb(204, 204, 204)'
77
+ '--icon-compass-south-color': 'rgb(204, 204, 204)',
78
+ '--menu-gap': '4px'
70
79
  } as const satisfies Required<DeckWidgetTheme>;
71
80
 
72
81
  export const DarkGlassTheme = {
@@ -81,6 +90,8 @@ export const DarkGlassTheme = {
81
90
  '--button-backdrop-filter': 'blur(4px)',
82
91
  '--button-icon-idle': 'rgba(158, 157, 168, 1)',
83
92
  '--button-icon-hover': 'rgba(215, 214, 229, 1)',
93
+ '--button-text': 'rgb(215, 214, 229, 1)',
84
94
  '--icon-compass-north-color': 'rgb(240, 92, 68)',
85
- '--icon-compass-south-color': 'rgb(200, 199, 209)'
95
+ '--icon-compass-south-color': 'rgb(200, 199, 209)',
96
+ '--menu-gap': '4px'
86
97
  } as const satisfies Required<DeckWidgetTheme>;
@@ -2,45 +2,24 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {Widget, type WidgetPlacement} from '@deck.gl/core';
5
+ import {Widget, type WidgetPlacement, type WidgetProps} from '@deck.gl/core';
6
6
  import {render} from 'preact';
7
+ import {IconButton} from './lib/components/icon-button';
7
8
 
8
- export type TimelineWidgetProps = {
9
- /**
10
- * Widget id
11
- */
12
- id?: string;
13
- /**
14
- * CSS inline style overrides.
15
- */
16
- style?: Partial<CSSStyleDeclaration>;
17
- /**
18
- * Additional CSS class.
19
- */
20
- className?: string;
21
- /**
22
- * Widget placement.
23
- */
9
+ export type TimelineWidgetProps = WidgetProps & {
10
+ /** Widget positioning within the view. Default 'bottom-left'. */
24
11
  placement?: WidgetPlacement;
25
- /**
26
- * Slider timeRange [min, max].
27
- */
12
+ /** View to attach to and interact with. Required when using multiple views. */
13
+ viewId?: string | null;
14
+ /** Slider timeRange [min, max]. */
28
15
  timeRange?: [number, number];
29
- /**
30
- * Slider step.
31
- */
16
+ /** Slider step. */
32
17
  step?: number;
33
- /**
34
- * Initial slider value.
35
- */
18
+ /** Initial slider value. */
36
19
  initialTime?: number;
37
- /**
38
- * Callback when value changes.
39
- */
20
+ /** Callback when value changes. */
40
21
  onTimeChange?: (value: number) => void;
41
- /**
42
- * Play interval in milliseconds.
43
- */
22
+ /** Play interval in milliseconds. */
44
23
  playInterval?: number;
45
24
  };
46
25
 
@@ -56,7 +35,8 @@ export class TimelineWidget extends Widget<TimelineWidgetProps> {
56
35
  static defaultProps: Required<TimelineWidgetProps> = {
57
36
  ...Widget.defaultProps,
58
37
  id: 'timeline',
59
- placement: 'bottom-left' as const,
38
+ placement: 'bottom-left',
39
+ viewId: null,
60
40
  timeRange: [0, 100],
61
41
  step: 1,
62
42
  initialTime: undefined!,
@@ -67,10 +47,12 @@ export class TimelineWidget extends Widget<TimelineWidgetProps> {
67
47
  constructor(props: TimelineWidgetProps = {}) {
68
48
  super(props, TimelineWidget.defaultProps);
69
49
  this.currentTime = this.props.initialTime ?? this.props.timeRange[0];
50
+ this.setProps(this.props);
70
51
  }
71
52
 
72
53
  setProps(props: Partial<TimelineWidgetProps>): void {
73
- this.placement = props.placement || this.placement;
54
+ this.placement = props.placement ?? this.placement;
55
+ this.viewId = props.viewId ?? this.viewId;
74
56
  super.setProps(props);
75
57
  }
76
58
 
@@ -86,14 +68,9 @@ export class TimelineWidget extends Widget<TimelineWidgetProps> {
86
68
  onRenderHTML(rootElement: HTMLElement): void {
87
69
  render(
88
70
  <div style={{display: 'flex', alignItems: 'center', pointerEvents: 'auto'}}>
89
- <button
90
- type="button"
91
- className="timeline-play-pause"
92
- title={this.playing ? 'Pause' : 'Play'}
93
- onClick={this.handlePlayPause}
94
- >
95
- {this.playing ? '⏸' : '▶'}
96
- </button>
71
+ <IconButton label={this.playing ? 'Pause' : 'Play'} onClick={this.handlePlayPause}>
72
+ <div className="text">{this.playing ? '⏸' : '▶'}</div>
73
+ </IconButton>
97
74
  <input
98
75
  type="range"
99
76
  className="timeline-slider"