@deck.gl/widgets 9.3.0-alpha.5 → 9.3.0-beta.1

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 (55) hide show
  1. package/dist/compass-widget.d.ts +12 -0
  2. package/dist/compass-widget.d.ts.map +1 -1
  3. package/dist/compass-widget.js +9 -3
  4. package/dist/compass-widget.js.map +1 -1
  5. package/dist/dist.dev.js +205 -69
  6. package/dist/fullscreen-widget.d.ts +5 -0
  7. package/dist/fullscreen-widget.d.ts.map +1 -1
  8. package/dist/fullscreen-widget.js +18 -8
  9. package/dist/fullscreen-widget.js.map +1 -1
  10. package/dist/geocoder-widget.d.ts +13 -0
  11. package/dist/geocoder-widget.d.ts.map +1 -1
  12. package/dist/geocoder-widget.js +13 -1
  13. package/dist/geocoder-widget.js.map +1 -1
  14. package/dist/gimbal-widget.d.ts +12 -0
  15. package/dist/gimbal-widget.d.ts.map +1 -1
  16. package/dist/gimbal-widget.js +4 -1
  17. package/dist/gimbal-widget.js.map +1 -1
  18. package/dist/index.cjs +218 -70
  19. package/dist/index.cjs.map +2 -2
  20. package/dist/loading-widget.d.ts +5 -0
  21. package/dist/loading-widget.d.ts.map +1 -1
  22. package/dist/loading-widget.js +4 -1
  23. package/dist/loading-widget.js.map +1 -1
  24. package/dist/reset-view-widget.d.ts +9 -0
  25. package/dist/reset-view-widget.d.ts.map +1 -1
  26. package/dist/reset-view-widget.js +4 -1
  27. package/dist/reset-view-widget.js.map +1 -1
  28. package/dist/stats-widget.d.ts +18 -3
  29. package/dist/stats-widget.d.ts.map +1 -1
  30. package/dist/stats-widget.js +29 -12
  31. package/dist/stats-widget.js.map +1 -1
  32. package/dist/theme-widget.d.ts +20 -2
  33. package/dist/theme-widget.d.ts.map +1 -1
  34. package/dist/theme-widget.js +38 -14
  35. package/dist/theme-widget.js.map +1 -1
  36. package/dist/timeline-widget.d.ts +34 -3
  37. package/dist/timeline-widget.d.ts.map +1 -1
  38. package/dist/timeline-widget.js +98 -24
  39. package/dist/timeline-widget.js.map +1 -1
  40. package/dist/zoom-widget.d.ts +13 -1
  41. package/dist/zoom-widget.d.ts.map +1 -1
  42. package/dist/zoom-widget.js +7 -4
  43. package/dist/zoom-widget.js.map +1 -1
  44. package/dist.min.js +2 -2
  45. package/package.json +3 -3
  46. package/src/compass-widget.tsx +23 -3
  47. package/src/fullscreen-widget.tsx +24 -9
  48. package/src/geocoder-widget.tsx +23 -1
  49. package/src/gimbal-widget.tsx +17 -1
  50. package/src/loading-widget.tsx +11 -1
  51. package/src/reset-view-widget.tsx +15 -1
  52. package/src/stats-widget.tsx +42 -13
  53. package/src/theme-widget.tsx +56 -17
  54. package/src/timeline-widget.tsx +123 -24
  55. package/src/zoom-widget.tsx +20 -4
package/dist/index.cjs CHANGED
@@ -110,7 +110,8 @@ var ZoomWidget = class extends import_core.Widget {
110
110
  const ui = (0, import_jsx_runtime3.jsxs)(ButtonGroup, { orientation: this.props.orientation, children: [(0, import_jsx_runtime3.jsx)(IconButton, { onClick: () => this.handleZoomIn(), label: this.props.zoomInLabel, className: "deck-widget-zoom-in" }), (0, import_jsx_runtime3.jsx)(IconButton, { onClick: () => this.handleZoomOut(), label: this.props.zoomOutLabel, className: "deck-widget-zoom-out" })] });
111
111
  (0, import_preact.render)(ui, rootElement);
112
112
  }
113
- handleZoom(viewId, nextZoom) {
113
+ handleZoom(viewId, nextZoom, delta) {
114
+ var _a, _b;
114
115
  const viewState = this.getViewState(viewId);
115
116
  if (viewState) {
116
117
  const { minZoom, maxZoom } = viewState;
@@ -121,6 +122,7 @@ var ZoomWidget = class extends import_core.Widget {
121
122
  nextZoom = Math.min(maxZoom, nextZoom);
122
123
  }
123
124
  }
125
+ (_b = (_a = this.props).onZoom) == null ? void 0 : _b.call(_a, { viewId, delta, zoom: nextZoom });
124
126
  const nextViewState = {
125
127
  ...viewState,
126
128
  zoom: nextZoom
@@ -138,7 +140,7 @@ var ZoomWidget = class extends import_core.Widget {
138
140
  const viewIds = this.viewId ? [this.viewId] : ((_a = this.deck) == null ? void 0 : _a.getViews().map((v) => v.id)) ?? [];
139
141
  for (const viewId of viewIds) {
140
142
  const viewState = this.getViewState(viewId);
141
- this.handleZoom(viewId, viewState.zoom + 1);
143
+ this.handleZoom(viewId, viewState.zoom + 1, 1);
142
144
  }
143
145
  }
144
146
  handleZoomOut() {
@@ -146,7 +148,7 @@ var ZoomWidget = class extends import_core.Widget {
146
148
  const viewIds = this.viewId ? [this.viewId] : ((_a = this.deck) == null ? void 0 : _a.getViews().map((v) => v.id)) ?? [];
147
149
  for (const viewId of viewIds) {
148
150
  const viewState = this.getViewState(viewId);
149
- this.handleZoom(viewId, viewState.zoom - 1);
151
+ this.handleZoom(viewId, viewState.zoom - 1, -1);
150
152
  }
151
153
  }
152
154
  };
@@ -158,7 +160,9 @@ ZoomWidget.defaultProps = {
158
160
  transitionDuration: 200,
159
161
  zoomInLabel: "Zoom In",
160
162
  zoomOutLabel: "Zoom Out",
161
- viewId: null
163
+ viewId: null,
164
+ onZoom: () => {
165
+ }
162
166
  };
163
167
 
164
168
  // dist/reset-view-widget.js
@@ -186,7 +190,7 @@ var ResetViewWidget = class extends import_core2.Widget {
186
190
  this.resetViewState(initialViewState);
187
191
  }
188
192
  resetViewState(viewState) {
189
- var _a;
193
+ var _a, _b, _c;
190
194
  const viewIds = this.viewId ? [this.viewId] : ((_a = this.deck) == null ? void 0 : _a.getViews().map((v) => v.id)) ?? [];
191
195
  for (const viewId of viewIds) {
192
196
  const nextViewState = {
@@ -195,6 +199,7 @@ var ResetViewWidget = class extends import_core2.Widget {
195
199
  // transitionDuration: this.props.transitionDuration,
196
200
  // transitionInterpolator: new FlyToInterpolator()
197
201
  };
202
+ (_c = (_b = this.props).onReset) == null ? void 0 : _c.call(_b, { viewId, viewState: nextViewState });
198
203
  this.setViewState(viewId, nextViewState);
199
204
  }
200
205
  }
@@ -205,7 +210,9 @@ ResetViewWidget.defaultProps = {
205
210
  placement: "top-left",
206
211
  label: "Reset View",
207
212
  initialViewState: void 0,
208
- viewId: null
213
+ viewId: null,
214
+ onReset: () => {
215
+ }
209
216
  };
210
217
 
211
218
  // dist/gimbal-widget.js
@@ -252,9 +259,11 @@ var GimbalWidget = class extends import_core3.Widget {
252
259
  this.updateHTML();
253
260
  }
254
261
  resetOrbitView(viewport) {
262
+ var _a, _b;
255
263
  const viewId = this.viewId || (viewport == null ? void 0 : viewport.id) || "OrbitView";
256
264
  const viewState = this.getViewState(viewId);
257
265
  if ("rotationOrbit" in viewState || "rotationX" in viewState) {
266
+ (_b = (_a = this.props).onReset) == null ? void 0 : _b.call(_a, { viewId, rotationOrbit: 0, rotationX: 0 });
258
267
  const nextViewState = {
259
268
  ...viewState,
260
269
  rotationOrbit: 0,
@@ -289,7 +298,9 @@ GimbalWidget.defaultProps = {
289
298
  viewId: null,
290
299
  label: "Gimbal",
291
300
  strokeWidth: 1.5,
292
- transitionDuration: 200
301
+ transitionDuration: 200,
302
+ onReset: () => {
303
+ }
293
304
  };
294
305
  function normalizeAndClampAngle(angle) {
295
306
  let normalized = ((angle + 180) % 360 + 360) % 360 - 180;
@@ -349,13 +360,18 @@ var CompassWidget = class extends import_core4.Widget {
349
360
  return [0, 0];
350
361
  }
351
362
  handleCompassReset(viewport) {
363
+ var _a, _b;
352
364
  const viewId = this.viewId || viewport.id;
353
365
  if (viewport instanceof import_core4.WebMercatorViewport) {
354
366
  const viewState = this.getViewState(viewId);
367
+ const resetPitch = this.getRotation(viewport)[0] === 0;
368
+ const nextBearing = 0;
369
+ const nextPitch = resetPitch ? 0 : viewport.pitch;
370
+ (_b = (_a = this.props).onReset) == null ? void 0 : _b.call(_a, { viewId, bearing: nextBearing, pitch: nextPitch });
355
371
  const nextViewState = {
356
372
  ...viewState,
357
- bearing: 0,
358
- ...this.getRotation(viewport)[0] === 0 ? { pitch: 0 } : {},
373
+ bearing: nextBearing,
374
+ ...resetPitch ? { pitch: nextPitch } : {},
359
375
  transitionDuration: this.props.transitionDuration,
360
376
  transitionInterpolator: new import_core4.FlyToInterpolator()
361
377
  };
@@ -369,7 +385,9 @@ CompassWidget.defaultProps = {
369
385
  placement: "top-left",
370
386
  viewId: null,
371
387
  label: "Reset Compass",
372
- transitionDuration: 200
388
+ transitionDuration: 200,
389
+ onReset: () => {
390
+ }
373
391
  };
374
392
 
375
393
  // dist/scale-widget.js
@@ -791,9 +809,19 @@ var GeocoderWidget = class extends import_core6.Widget {
791
809
  }), (0, import_jsx_runtime9.jsx)(DropdownMenu, { menuItems, onSelect: this.handleSelect }), this.geocodeHistory.errorText && (0, import_jsx_runtime9.jsx)("div", { className: "deck-widget-geocoder-error", children: this.geocodeHistory.errorText })] }), rootElement);
792
810
  }
793
811
  flyTo(viewState) {
794
- var _a;
812
+ var _a, _b, _c;
795
813
  const viewIds = this.viewId ? [this.viewId] : ((_a = this.deck) == null ? void 0 : _a.getViews().map((v) => v.id)) ?? [];
796
814
  for (const viewId of viewIds) {
815
+ if ("longitude" in viewState && "latitude" in viewState) {
816
+ (_c = (_b = this.props).onGeocode) == null ? void 0 : _c.call(_b, {
817
+ viewId,
818
+ coordinates: {
819
+ longitude: viewState.longitude,
820
+ latitude: viewState.latitude,
821
+ zoom: viewState.zoom
822
+ }
823
+ });
824
+ }
797
825
  const currentViewState = this.getViewState(viewId);
798
826
  const nextViewState = {
799
827
  ...currentViewState,
@@ -817,7 +845,9 @@ GeocoderWidget.defaultProps = {
817
845
  geocoder: "coordinates",
818
846
  customGeocoder: CoordinatesGeocoder,
819
847
  apiKey: "",
820
- _geolocation: false
848
+ _geolocation: false,
849
+ onGeocode: () => {
850
+ }
821
851
  };
822
852
  function getGeocoder(props) {
823
853
  switch (props.geocoder) {
@@ -858,9 +888,10 @@ var FullscreenWidget = class extends import_core8.Widget {
858
888
  document.removeEventListener("fullscreenchange", this.onFullscreenChange.bind(this));
859
889
  }
860
890
  onRenderHTML(rootElement) {
891
+ const isFullscreen = this.getFullscreen();
861
892
  (0, import_preact7.render)((0, import_jsx_runtime10.jsx)(IconButton, { onClick: () => {
862
893
  this.handleClick().catch((err) => import_core8.log.error(err)());
863
- }, label: this.fullscreen ? this.props.exitLabel : this.props.enterLabel, className: this.fullscreen ? "deck-widget-fullscreen-exit" : "deck-widget-fullscreen-enter" }), rootElement);
894
+ }, label: isFullscreen ? this.props.exitLabel : this.props.enterLabel, className: isFullscreen ? "deck-widget-fullscreen-exit" : "deck-widget-fullscreen-enter" }), rootElement);
864
895
  }
865
896
  setProps(props) {
866
897
  this.placement = props.placement ?? this.placement;
@@ -871,21 +902,25 @@ var FullscreenWidget = class extends import_core8.Widget {
871
902
  var _a, _b, _c;
872
903
  return this.props.container || ((_a = this.deck) == null ? void 0 : _a.props.parent) || ((_c = (_b = this.deck) == null ? void 0 : _b.getCanvas()) == null ? void 0 : _c.parentElement);
873
904
  }
905
+ getFullscreen() {
906
+ return this.fullscreen;
907
+ }
874
908
  onFullscreenChange() {
875
- const prevFullscreen = this.fullscreen;
909
+ var _a, _b;
876
910
  const fullscreen = document.fullscreenElement === this.getContainer();
877
- if (prevFullscreen !== fullscreen) {
878
- this.fullscreen = !this.fullscreen;
911
+ if (fullscreen !== this.fullscreen) {
912
+ this.fullscreen = fullscreen;
913
+ (_b = (_a = this.props).onFullscreenChange) == null ? void 0 : _b.call(_a, fullscreen);
914
+ this.updateHTML();
879
915
  }
880
- this.updateHTML();
881
916
  }
882
917
  async handleClick() {
883
- if (this.fullscreen) {
918
+ const isFullscreen = this.getFullscreen();
919
+ if (isFullscreen) {
884
920
  await this.exitFullscreen();
885
921
  } else {
886
922
  await this.requestFullscreen();
887
923
  }
888
- this.updateHTML();
889
924
  }
890
925
  async requestFullscreen() {
891
926
  const container = this.getContainer();
@@ -903,8 +938,11 @@ var FullscreenWidget = class extends import_core8.Widget {
903
938
  }
904
939
  }
905
940
  togglePseudoFullscreen() {
906
- var _a;
941
+ var _a, _b, _c;
907
942
  (_a = this.getContainer()) == null ? void 0 : _a.classList.toggle("deck-pseudo-fullscreen");
943
+ this.fullscreen = !this.fullscreen;
944
+ (_c = (_b = this.props).onFullscreenChange) == null ? void 0 : _c.call(_b, this.fullscreen);
945
+ this.updateHTML();
908
946
  }
909
947
  };
910
948
  FullscreenWidget.defaultProps = {
@@ -914,7 +952,9 @@ FullscreenWidget.defaultProps = {
914
952
  viewId: null,
915
953
  enterLabel: "Enter Fullscreen",
916
954
  exitLabel: "Exit Fullscreen",
917
- container: void 0
955
+ container: void 0,
956
+ onFullscreenChange: () => {
957
+ }
918
958
  };
919
959
 
920
960
  // dist/splitter-widget.js
@@ -2017,74 +2057,124 @@ var import_jsx_runtime22 = require("preact/jsx-runtime");
2017
2057
  var import_core17 = require("@deck.gl/core");
2018
2058
  var import_preact16 = require("preact");
2019
2059
  var TimelineWidget = class extends import_core17.Widget {
2060
+ /**
2061
+ * Returns the current time value.
2062
+ * In controlled mode, returns the time prop.
2063
+ * In uncontrolled mode, returns the internal state.
2064
+ */
2065
+ getTime() {
2066
+ return this.props.time ?? this.currentTime;
2067
+ }
2068
+ /**
2069
+ * Returns the current playing state.
2070
+ * In controlled mode, returns the playing prop.
2071
+ * In uncontrolled mode, returns the internal state.
2072
+ */
2073
+ getPlaying() {
2074
+ return this.props.playing ?? this._playing;
2075
+ }
2020
2076
  constructor(props = {}) {
2021
2077
  var _a;
2022
2078
  super(props);
2023
2079
  this.id = "timeline";
2024
2080
  this.className = "deck-widget-timeline";
2025
2081
  this.placement = "fill";
2026
- this.playing = false;
2082
+ this._playing = false;
2027
2083
  this.timerId = null;
2028
2084
  this.handlePlayPause = () => {
2029
- if (this.playing) {
2030
- this.stop();
2031
- } else {
2032
- this.play();
2085
+ var _a2, _b;
2086
+ const isPlaying = this.getPlaying();
2087
+ const nextPlaying = !isPlaying;
2088
+ (_b = (_a2 = this.props).onPlayingChange) == null ? void 0 : _b.call(_a2, nextPlaying);
2089
+ if (this.props.playing === void 0) {
2090
+ if (nextPlaying) {
2091
+ this.play();
2092
+ } else {
2093
+ this.stop();
2094
+ }
2033
2095
  }
2034
2096
  };
2035
2097
  this.handleTimeChange = ([value]) => {
2036
2098
  var _a2;
2037
- this.currentTime = value;
2038
- (_a2 = this.props.timeline) == null ? void 0 : _a2.setTime(value);
2039
2099
  this.props.onTimeChange(value);
2040
- this.updateHTML();
2100
+ if (this.props.time === void 0) {
2101
+ this.currentTime = value;
2102
+ (_a2 = this.props.timeline) == null ? void 0 : _a2.setTime(value);
2103
+ this.updateHTML();
2104
+ }
2041
2105
  };
2042
2106
  this.tick = () => {
2107
+ var _a2, _b, _c;
2043
2108
  const { timeRange: [min, max], step, loop } = this.props;
2044
2109
  if (step > 0) {
2045
- let next = Math.round(this.currentTime / step) * step + step;
2110
+ const currentTime = this.getTime();
2111
+ let next = Math.round(currentTime / step) * step + step;
2046
2112
  if (next > max) {
2047
- if (this.currentTime < max) {
2113
+ if (currentTime < max) {
2048
2114
  next = max;
2049
2115
  } else if (loop) {
2050
2116
  next = min;
2051
2117
  } else {
2052
2118
  next = max;
2053
- this.playing = false;
2119
+ this._playing = false;
2120
+ (_b = (_a2 = this.props).onPlayingChange) == null ? void 0 : _b.call(_a2, false);
2054
2121
  }
2055
2122
  }
2056
- this.currentTime = next;
2057
2123
  this.props.onTimeChange(next);
2124
+ if (this.props.time === void 0) {
2125
+ this.currentTime = next;
2126
+ (_c = this.props.timeline) == null ? void 0 : _c.setTime(next);
2127
+ }
2058
2128
  this.updateHTML();
2059
2129
  }
2060
- if (this.playing) {
2130
+ if (this._playing) {
2061
2131
  this.timerId = window.setTimeout(this.tick, this.props.playInterval);
2062
2132
  } else {
2063
2133
  this.timerId = null;
2064
2134
  }
2065
2135
  };
2066
2136
  this.currentTime = this.props.initialTime ?? this.props.timeRange[0];
2067
- (_a = this.props.timeline) == null ? void 0 : _a.setTime(this.currentTime);
2137
+ const syncTime = this.props.time ?? this.currentTime;
2138
+ (_a = this.props.timeline) == null ? void 0 : _a.setTime(syncTime);
2068
2139
  this.setProps(this.props);
2069
2140
  }
2070
2141
  setProps(props) {
2142
+ var _a;
2143
+ const { playing: prevPlaying, time: prevTime } = this.props;
2071
2144
  this.viewId = props.viewId ?? this.viewId;
2072
2145
  super.setProps(props);
2146
+ if (props.time !== void 0 && props.time !== prevTime) {
2147
+ (_a = this.props.timeline) == null ? void 0 : _a.setTime(props.time);
2148
+ }
2149
+ if (props.playing !== void 0 && props.playing !== prevPlaying) {
2150
+ if (props.playing && !this._playing) {
2151
+ this._startTimer();
2152
+ } else if (!props.playing && this._playing) {
2153
+ this._stopTimer();
2154
+ }
2155
+ }
2073
2156
  }
2074
2157
  onAdd() {
2075
- this.playing = false;
2158
+ var _a, _b;
2159
+ this._playing = false;
2076
2160
  this.timerId = null;
2077
- if (this.props.autoPlay)
2078
- this.play();
2161
+ if (this.props.autoPlay) {
2162
+ if (this.props.playing !== void 0) {
2163
+ (_b = (_a = this.props).onPlayingChange) == null ? void 0 : _b.call(_a, true);
2164
+ } else {
2165
+ this.play();
2166
+ }
2167
+ }
2079
2168
  }
2080
2169
  onRemove() {
2081
2170
  this.stop();
2082
2171
  }
2083
2172
  onRenderHTML(rootElement) {
2084
2173
  const { timeRange, step, formatLabel } = this.props;
2085
- const currentTime = this.currentTime;
2174
+ const isPlaying = this.getPlaying();
2175
+ const currentTime = this.getTime();
2086
2176
  rootElement.dataset.placement = this.props.placement;
2087
- (0, import_preact16.render)((0, import_jsx_runtime22.jsxs)("div", { className: "deck-widget-button-group", children: [this.playing ? (0, import_jsx_runtime22.jsx)(IconButton, { label: "Pause", className: "deck-widget-timeline-pause", onClick: this.handlePlayPause }) : (0, import_jsx_runtime22.jsx)(IconButton, { label: "Play", className: "deck-widget-timeline-play", onClick: this.handlePlayPause }), (0, import_jsx_runtime22.jsx)(RangeInput, { min: timeRange[0], max: timeRange[1], orientation: "horizontal", step, value: [currentTime, currentTime], onChange: this.handleTimeChange, decorations: [
2177
+ (0, import_preact16.render)((0, import_jsx_runtime22.jsxs)("div", { className: "deck-widget-button-group", children: [isPlaying ? (0, import_jsx_runtime22.jsx)(IconButton, { label: "Pause", className: "deck-widget-timeline-pause", onClick: this.handlePlayPause }) : (0, import_jsx_runtime22.jsx)(IconButton, { label: "Play", className: "deck-widget-timeline-play", onClick: this.handlePlayPause }), (0, import_jsx_runtime22.jsx)(RangeInput, { min: timeRange[0], max: timeRange[1], orientation: "horizontal", step, value: [currentTime, currentTime], onChange: this.handleTimeChange, decorations: [
2088
2178
  {
2089
2179
  position: [currentTime, currentTime + step],
2090
2180
  element: (0, import_jsx_runtime22.jsx)("div", { className: "deck-widget-timeline-label deck-widget-timeline-label--current", children: formatLabel(currentTime) })
@@ -2092,22 +2182,33 @@ var TimelineWidget = class extends import_core17.Widget {
2092
2182
  ] })] }), rootElement);
2093
2183
  }
2094
2184
  play() {
2095
- this.playing = true;
2185
+ var _a;
2186
+ this._playing = true;
2096
2187
  const { timeRange: [min, max] } = this.props;
2097
- if (this.currentTime >= max) {
2188
+ if (this.props.time === void 0 && this.getTime() >= max) {
2098
2189
  this.currentTime = min;
2099
2190
  this.props.onTimeChange(min);
2191
+ (_a = this.props.timeline) == null ? void 0 : _a.setTime(min);
2100
2192
  }
2101
2193
  this.updateHTML();
2102
2194
  this.tick();
2103
2195
  }
2104
2196
  stop() {
2105
- this.playing = false;
2197
+ this._stopTimer();
2198
+ this.updateHTML();
2199
+ }
2200
+ /** Start the playback timer (used internally) */
2201
+ _startTimer() {
2202
+ this._playing = true;
2203
+ this.tick();
2204
+ }
2205
+ /** Stop the playback timer (used internally) */
2206
+ _stopTimer() {
2207
+ this._playing = false;
2106
2208
  if (this.timerId !== null) {
2107
2209
  window.clearTimeout(this.timerId);
2108
2210
  this.timerId = null;
2109
2211
  }
2110
- this.updateHTML();
2111
2212
  }
2112
2213
  };
2113
2214
  TimelineWidget.defaultProps = {
@@ -2119,11 +2220,15 @@ TimelineWidget.defaultProps = {
2119
2220
  timeRange: [0, 100],
2120
2221
  step: 1,
2121
2222
  initialTime: void 0,
2223
+ time: void 0,
2122
2224
  onTimeChange: () => {
2123
2225
  },
2124
2226
  autoPlay: false,
2125
2227
  loop: false,
2126
2228
  playInterval: 1e3,
2229
+ playing: void 0,
2230
+ onPlayingChange: () => {
2231
+ },
2127
2232
  formatLabel: String
2128
2233
  };
2129
2234
 
@@ -2313,44 +2418,64 @@ var ThemeWidget = class extends import_core20.Widget {
2313
2418
  }
2314
2419
  // eslint-disable-next-line complexity
2315
2420
  setProps(props) {
2316
- const { lightModeTheme, darkModeTheme } = this.props;
2421
+ const { lightModeTheme, darkModeTheme, themeMode: prevThemeMode } = this.props;
2317
2422
  this.placement = props.placement ?? this.placement;
2318
2423
  this.viewId = props.viewId ?? this.viewId;
2319
2424
  super.setProps(props);
2320
- switch (this.themeMode) {
2425
+ const currentMode = this.getThemeMode();
2426
+ if (props.themeMode !== void 0 && props.themeMode !== prevThemeMode) {
2427
+ this._applyTheme(props.themeMode);
2428
+ return;
2429
+ }
2430
+ switch (currentMode) {
2321
2431
  case "light":
2322
2432
  if (props.lightModeTheme && !(0, import_core19._deepEqual)(props.lightModeTheme, lightModeTheme, 1)) {
2323
- this._setThemeMode("light");
2433
+ this._applyTheme("light");
2324
2434
  }
2325
2435
  break;
2326
2436
  case "dark":
2327
2437
  if (props.darkModeTheme && !(0, import_core19._deepEqual)(props.darkModeTheme, darkModeTheme, 1)) {
2328
- this._setThemeMode("dark");
2438
+ this._applyTheme("dark");
2329
2439
  }
2330
2440
  break;
2331
2441
  default:
2332
- import_core19.log.warn(`Invalid theme mode ${this.themeMode}`)();
2442
+ import_core19.log.warn(`Invalid theme mode ${currentMode}`)();
2333
2443
  }
2334
2444
  }
2335
2445
  onRenderHTML(rootElement) {
2336
2446
  const { lightModeLabel, darkModeLabel } = this.props;
2337
- (0, import_preact18.render)((0, import_jsx_runtime24.jsx)(IconButton, { onClick: this._handleClick.bind(this), label: this.themeMode === "dark" ? darkModeLabel : lightModeLabel, className: this.themeMode === "dark" ? "deck-widget-moon" : "deck-widget-sun" }), rootElement);
2447
+ const currentMode = this.getThemeMode();
2448
+ (0, import_preact18.render)((0, import_jsx_runtime24.jsx)(IconButton, { onClick: this._handleClick.bind(this), label: currentMode === "dark" ? darkModeLabel : lightModeLabel, className: currentMode === "dark" ? "deck-widget-moon" : "deck-widget-sun" }), rootElement);
2338
2449
  }
2339
2450
  onAdd() {
2340
- this._setThemeMode(this.themeMode);
2451
+ this._applyTheme(this.getThemeMode());
2452
+ }
2453
+ /**
2454
+ * Returns the current theme mode.
2455
+ * In controlled mode, returns the themeMode prop.
2456
+ * In uncontrolled mode, returns the internal state.
2457
+ */
2458
+ getThemeMode() {
2459
+ return this.props.themeMode ?? this.themeMode;
2341
2460
  }
2342
2461
  _handleClick() {
2343
- const newThemeMode = this.themeMode === "dark" ? "light" : "dark";
2344
- this._setThemeMode(newThemeMode);
2462
+ var _a, _b;
2463
+ const currentMode = this.getThemeMode();
2464
+ const nextMode = currentMode === "dark" ? "light" : "dark";
2465
+ (_b = (_a = this.props).onThemeModeChange) == null ? void 0 : _b.call(_a, nextMode);
2466
+ if (this.props.themeMode === void 0) {
2467
+ this.themeMode = nextMode;
2468
+ this._applyTheme(nextMode);
2469
+ }
2345
2470
  }
2346
- _setThemeMode(themeMode) {
2471
+ /** Apply theme styling without changing internal state */
2472
+ _applyTheme(themeMode) {
2347
2473
  var _a;
2348
- this.themeMode = themeMode;
2349
2474
  const container = (_a = this.rootElement) == null ? void 0 : _a.closest(".deck-widget-container");
2350
2475
  if (container) {
2351
2476
  const themeStyle = themeMode === "dark" ? this.props.darkModeTheme : this.props.lightModeTheme;
2352
2477
  (0, import_core19._applyStyles)(container, themeStyle);
2353
- const label = this.themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
2478
+ const label = themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
2354
2479
  import_core19.log.log(1, `Switched theme to ${label}`, themeStyle)();
2355
2480
  this.updateHTML();
2356
2481
  }
@@ -2376,7 +2501,10 @@ ThemeWidget.defaultProps = {
2376
2501
  lightModeTheme: LightGlassTheme,
2377
2502
  darkModeLabel: "Dark Mode",
2378
2503
  darkModeTheme: DarkGlassTheme,
2379
- initialThemeMode: "auto"
2504
+ initialThemeMode: "auto",
2505
+ themeMode: void 0,
2506
+ onThemeModeChange: () => {
2507
+ }
2380
2508
  };
2381
2509
 
2382
2510
  // dist/loading-widget.js
@@ -2404,9 +2532,11 @@ var LoadingWidget = class extends import_core21.Widget {
2404
2532
  );
2405
2533
  }
2406
2534
  onRedraw({ layers }) {
2535
+ var _a, _b;
2407
2536
  const loading = layers.some((layer) => !layer.isLoaded);
2408
2537
  if (loading !== this.loading) {
2409
2538
  this.loading = loading;
2539
+ (_b = (_a = this.props).onLoadingChange) == null ? void 0 : _b.call(_a, loading);
2410
2540
  this.updateHTML();
2411
2541
  }
2412
2542
  }
@@ -2419,7 +2549,9 @@ LoadingWidget.defaultProps = {
2419
2549
  id: "loading",
2420
2550
  placement: "top-left",
2421
2551
  viewId: null,
2422
- label: "Loading layer data"
2552
+ label: "Loading layer data",
2553
+ onLoadingChange: () => {
2554
+ }
2423
2555
  };
2424
2556
 
2425
2557
  // dist/stats-widget.js
@@ -2444,15 +2576,28 @@ var DEFAULT_FORMATTERS = {
2444
2576
  memory: (stat) => `${stat.name}: ${formatMemory(stat.count)}`
2445
2577
  };
2446
2578
  var StatsWidget = class extends import_core22.Widget {
2579
+ /**
2580
+ * Returns the current expanded state.
2581
+ * In controlled mode, returns the expanded prop.
2582
+ * In uncontrolled mode, returns the internal state.
2583
+ */
2584
+ getExpanded() {
2585
+ return this.props.expanded ?? this._expanded;
2586
+ }
2447
2587
  constructor(props = {}) {
2448
2588
  super(props);
2449
2589
  this.className = "deck-widget-stats";
2450
2590
  this.placement = "top-left";
2451
2591
  this._counter = 0;
2452
- this.collapsed = true;
2453
- this._toggleCollapsed = () => {
2454
- this.collapsed = !this.collapsed;
2455
- this.updateHTML();
2592
+ this._expanded = false;
2593
+ this._toggleExpanded = () => {
2594
+ var _a, _b;
2595
+ const nextExpanded = !this.getExpanded();
2596
+ (_b = (_a = this.props).onExpandedChange) == null ? void 0 : _b.call(_a, nextExpanded);
2597
+ if (this.props.expanded === void 0) {
2598
+ this._expanded = nextExpanded;
2599
+ this.updateHTML();
2600
+ }
2456
2601
  };
2457
2602
  this._getFps = () => {
2458
2603
  var _a;
@@ -2460,7 +2605,7 @@ var StatsWidget = class extends import_core22.Widget {
2460
2605
  };
2461
2606
  this._formatters = { ...DEFAULT_FORMATTERS };
2462
2607
  this._resetOnUpdate = { ...this.props.resetOnUpdate };
2463
- this.collapsed = !props.defaultIsExpanded;
2608
+ this._expanded = Boolean(props.initialExpanded);
2464
2609
  this.setProps(props);
2465
2610
  }
2466
2611
  setProps(props) {
@@ -2483,9 +2628,9 @@ var StatsWidget = class extends import_core22.Widget {
2483
2628
  }
2484
2629
  }
2485
2630
  onRenderHTML(rootElement) {
2486
- const collapsed = this.collapsed;
2487
- if (collapsed) {
2488
- (0, import_preact20.render)((0, import_jsx_runtime26.jsx)(FpsIcon, { getFps: this._getFps, onClick: this._toggleCollapsed }), rootElement);
2631
+ const isExpanded = this.getExpanded();
2632
+ if (!isExpanded) {
2633
+ (0, import_preact20.render)((0, import_jsx_runtime26.jsx)(FpsIcon, { getFps: this._getFps, onClick: this._toggleExpanded }), rootElement);
2489
2634
  return;
2490
2635
  }
2491
2636
  const stats = this._getStats();
@@ -2503,10 +2648,10 @@ var StatsWidget = class extends import_core22.Widget {
2503
2648
  });
2504
2649
  });
2505
2650
  }
2506
- (0, import_preact20.render)((0, import_jsx_runtime26.jsxs)("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [(0, import_jsx_runtime26.jsxs)("div", { className: "deck-widget-stats-header", style: { cursor: "pointer", pointerEvents: "auto" }, onClick: this._toggleCollapsed, children: [(0, import_jsx_runtime26.jsx)("b", { children: title }), deviceLabel && (0, import_jsx_runtime26.jsx)("span", { className: "deck-widget-stats-device", children: deviceLabel }), (0, import_jsx_runtime26.jsx)("button", { className: "deck-widget-dropdown-button", children: (0, import_jsx_runtime26.jsx)("span", { className: "deck-widget-dropdown-icon open" }) })] }), (0, import_jsx_runtime26.jsx)("div", { className: "deck-widget-stats-content", children: items })] }), rootElement);
2651
+ (0, import_preact20.render)((0, import_jsx_runtime26.jsxs)("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [(0, import_jsx_runtime26.jsxs)("div", { className: "deck-widget-stats-header", style: { cursor: "pointer", pointerEvents: "auto" }, onClick: this._toggleExpanded, children: [(0, import_jsx_runtime26.jsx)("b", { children: title }), deviceLabel && (0, import_jsx_runtime26.jsx)("span", { className: "deck-widget-stats-device", children: deviceLabel }), (0, import_jsx_runtime26.jsx)("button", { className: "deck-widget-dropdown-button", children: (0, import_jsx_runtime26.jsx)("span", { className: "deck-widget-dropdown-icon open" }) })] }), (0, import_jsx_runtime26.jsx)("div", { className: "deck-widget-stats-content", children: items })] }), rootElement);
2507
2652
  }
2508
2653
  onRedraw() {
2509
- if (!this.collapsed) {
2654
+ if (this.getExpanded()) {
2510
2655
  const framesPerUpdate = Math.max(1, this.props.framesPerUpdate || 1);
2511
2656
  if (this._counter++ % framesPerUpdate === 0) {
2512
2657
  this.updateHTML();
@@ -2561,13 +2706,16 @@ StatsWidget.defaultProps = {
2561
2706
  type: "deck",
2562
2707
  placement: "top-left",
2563
2708
  viewId: null,
2564
- defaultIsExpanded: false,
2709
+ initialExpanded: false,
2565
2710
  stats: void 0,
2566
2711
  title: "Stats",
2567
2712
  framesPerUpdate: 1,
2568
2713
  formatters: {},
2569
2714
  resetOnUpdate: {},
2570
- id: "stats"
2715
+ id: "stats",
2716
+ expanded: void 0,
2717
+ onExpandedChange: () => {
2718
+ }
2571
2719
  };
2572
2720
  function FpsIcon({ getFps, onClick }) {
2573
2721
  const [fps, setFps] = (0, import_hooks7.useState)(getFps());