@deck.gl/widgets 9.4.0-alpha.2 → 9.4.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 (118) hide show
  1. package/dist/compass-widget.d.ts +2 -0
  2. package/dist/compass-widget.d.ts.map +1 -1
  3. package/dist/compass-widget.js +8 -5
  4. package/dist/compass-widget.js.map +1 -1
  5. package/dist/context-menu-widget.d.ts.map +1 -1
  6. package/dist/dist.dev.js +3319 -3264
  7. package/dist/fullscreen-widget.d.ts +4 -0
  8. package/dist/fullscreen-widget.d.ts.map +1 -1
  9. package/dist/fullscreen-widget.js +3 -1
  10. package/dist/fullscreen-widget.js.map +1 -1
  11. package/dist/geocoder-widget.d.ts.map +1 -1
  12. package/dist/gimbal-widget.d.ts +2 -0
  13. package/dist/gimbal-widget.d.ts.map +1 -1
  14. package/dist/gimbal-widget.js +18 -15
  15. package/dist/gimbal-widget.js.map +1 -1
  16. package/dist/icon-widget.d.ts +2 -0
  17. package/dist/icon-widget.d.ts.map +1 -1
  18. package/dist/icon-widget.js +3 -2
  19. package/dist/icon-widget.js.map +1 -1
  20. package/dist/index.cjs +3444 -219
  21. package/dist/index.cjs.map +3 -4
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/info-widget.d.ts.map +1 -1
  27. package/dist/lib/components/button-group.d.ts.map +1 -1
  28. package/dist/lib/components/dropdown-menu.d.ts.map +1 -1
  29. package/dist/lib/components/icon-button.d.ts +2 -0
  30. package/dist/lib/components/icon-button.d.ts.map +1 -1
  31. package/dist/lib/components/icon-button.js +5 -2
  32. package/dist/lib/components/icon-button.js.map +1 -1
  33. package/dist/lib/components/popover.d.ts.map +1 -1
  34. package/dist/lib/components/tooltip.d.ts +14 -0
  35. package/dist/lib/components/tooltip.d.ts.map +1 -0
  36. package/dist/lib/components/tooltip.js +64 -0
  37. package/dist/lib/components/tooltip.js.map +1 -0
  38. package/dist/lib/components/user-content.d.ts.map +1 -1
  39. package/dist/loading-widget.d.ts +2 -0
  40. package/dist/loading-widget.d.ts.map +1 -1
  41. package/dist/loading-widget.js +2 -1
  42. package/dist/loading-widget.js.map +1 -1
  43. package/dist/popup-widget.d.ts.map +1 -1
  44. package/dist/reset-view-widget.d.ts +2 -0
  45. package/dist/reset-view-widget.d.ts.map +1 -1
  46. package/dist/reset-view-widget.js +2 -1
  47. package/dist/reset-view-widget.js.map +1 -1
  48. package/dist/screenshot-widget.d.ts +2 -0
  49. package/dist/screenshot-widget.d.ts.map +1 -1
  50. package/dist/screenshot-widget.js +2 -1
  51. package/dist/screenshot-widget.js.map +1 -1
  52. package/dist/scrollbar-widget.d.ts +6 -1
  53. package/dist/scrollbar-widget.d.ts.map +1 -1
  54. package/dist/scrollbar-widget.js +12 -3
  55. package/dist/scrollbar-widget.js.map +1 -1
  56. package/dist/selector-widget.d.ts +2 -0
  57. package/dist/selector-widget.d.ts.map +1 -1
  58. package/dist/selector-widget.js +2 -1
  59. package/dist/selector-widget.js.map +1 -1
  60. package/dist/splitter-widget.d.ts.map +1 -1
  61. package/dist/stylesheet.css +102 -116
  62. package/dist/theme-widget.d.ts +4 -0
  63. package/dist/theme-widget.d.ts.map +1 -1
  64. package/dist/theme-widget.js +4 -2
  65. package/dist/theme-widget.js.map +1 -1
  66. package/dist/themes.d.ts +5 -0
  67. package/dist/themes.d.ts.map +1 -1
  68. package/dist/themes.js +4 -0
  69. package/dist/themes.js.map +1 -1
  70. package/dist/timeline-widget.d.ts +4 -0
  71. package/dist/timeline-widget.d.ts.map +1 -1
  72. package/dist/timeline-widget.js +5 -3
  73. package/dist/timeline-widget.js.map +1 -1
  74. package/dist/toggle-widget.d.ts +4 -0
  75. package/dist/toggle-widget.d.ts.map +1 -1
  76. package/dist/toggle-widget.js +4 -2
  77. package/dist/toggle-widget.js.map +1 -1
  78. package/dist/zoom-widget.d.ts +4 -0
  79. package/dist/zoom-widget.d.ts.map +1 -1
  80. package/dist/zoom-widget.js +3 -1
  81. package/dist/zoom-widget.js.map +1 -1
  82. package/dist.min.js +2 -2
  83. package/package.json +4 -4
  84. package/src/compass-widget.tsx +31 -23
  85. package/src/fullscreen-widget.tsx +7 -0
  86. package/src/gimbal-widget.tsx +62 -54
  87. package/src/icon-widget.tsx +5 -1
  88. package/src/icons/add.svg +1 -0
  89. package/src/icons/chevron-down.svg +1 -0
  90. package/src/icons/chevron-left.svg +1 -0
  91. package/src/icons/chevron-right.svg +1 -0
  92. package/src/icons/chevron-up.svg +1 -0
  93. package/src/icons/close.svg +1 -0
  94. package/src/icons/dark-mode.svg +1 -0
  95. package/src/icons/dropdown.svg +1 -0
  96. package/src/icons/fullscreen-exit.svg +1 -0
  97. package/src/icons/fullscreen.svg +1 -0
  98. package/src/icons/light-mode.svg +1 -0
  99. package/src/icons/loading.svg +1 -0
  100. package/src/icons/pause.svg +1 -0
  101. package/src/icons/photo-camera.svg +1 -0
  102. package/src/icons/play-arrow.svg +1 -0
  103. package/src/icons/remove.svg +1 -0
  104. package/src/icons/reset-focus.svg +1 -0
  105. package/src/index.ts +1 -0
  106. package/src/lib/components/icon-button.tsx +17 -9
  107. package/src/lib/components/tooltip.tsx +116 -0
  108. package/src/loading-widget.tsx +4 -0
  109. package/src/reset-view-widget.tsx +4 -0
  110. package/src/screenshot-widget.tsx +4 -0
  111. package/src/scrollbar-widget.tsx +30 -4
  112. package/src/selector-widget.tsx +4 -0
  113. package/src/stylesheet.css +67 -114
  114. package/src/theme-widget.tsx +8 -1
  115. package/src/themes.ts +5 -0
  116. package/src/timeline-widget.tsx +10 -2
  117. package/src/toggle-widget.tsx +9 -0
  118. package/src/zoom-widget.tsx +8 -0
package/dist/index.cjs CHANGED
@@ -15,11 +15,11 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
19
19
 
20
20
  // dist/index.js
21
- var dist_exports = {};
22
- __export(dist_exports, {
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
23
  CompassWidget: () => CompassWidget,
24
24
  ContextMenuWidget: () => ContextMenuWidget,
25
25
  DarkGlassTheme: () => DarkGlassTheme,
@@ -54,13 +54,3162 @@ __export(dist_exports, {
54
54
  _SplitterWidget: () => SplitterWidget,
55
55
  _StatsWidget: () => StatsWidget,
56
56
  _TimelineWidget: () => TimelineWidget,
57
+ _Tooltip: () => Tooltip,
57
58
  buildViewsFromViewLayout: () => buildViewsFromViewLayout
58
59
  });
59
- module.exports = __toCommonJS(dist_exports);
60
+ module.exports = __toCommonJS(index_exports);
61
+
62
+ // dist/zoom-widget.js
63
+ var import_jsx_runtime4 = require("preact/jsx-runtime");
64
+
65
+ // ../core/src/utils/log.ts
66
+ var import_log = require("@probe.gl/log");
67
+ var defaultLogger = new import_log.Log({ id: "deck" });
68
+ var log_default = defaultLogger;
69
+
70
+ // ../core/src/lib/constants.ts
71
+ var import_mjolnir = require("mjolnir.js");
72
+ var COORDINATE_SYSTEM = {
73
+ /**
74
+ * `LNGLAT` if rendering into a geospatial viewport, `CARTESIAN` otherwise
75
+ */
76
+ DEFAULT: "default",
77
+ /**
78
+ * Positions are interpreted as [longitude, latitude, elevation]
79
+ * longitude/latitude are in degrees, elevation is in meters.
80
+ * Dimensions are in meters.
81
+ */
82
+ LNGLAT: "lnglat",
83
+ /**
84
+ * Positions are interpreted as [x, y, z] in meter offsets from the coordinate origin.
85
+ * Dimensions are in meters.
86
+ */
87
+ METER_OFFSETS: "meter-offsets",
88
+ /**
89
+ * Positions are interpreted as [deltaLng, deltaLat, elevation] from the coordinate origin.
90
+ * deltaLng/deltaLat are in degrees, elevation is in meters.
91
+ * Dimensions are in meters.
92
+ */
93
+ LNGLAT_OFFSETS: "lnglat-offsets",
94
+ /**
95
+ * Positions and dimensions are in the common units of the viewport.
96
+ */
97
+ CARTESIAN: "cartesian"
98
+ };
99
+ Object.defineProperty(COORDINATE_SYSTEM, "IDENTITY", {
100
+ get: () => {
101
+ log_default.deprecated("COORDINATE_SYSTEM.IDENTITY", "COORDINATE_SYSTEM.CARTESIAN")();
102
+ return COORDINATE_SYSTEM.CARTESIAN;
103
+ }
104
+ });
105
+ var PROJECTION_MODE = {
106
+ /**
107
+ * Render geospatial data in Web Mercator projection
108
+ */
109
+ WEB_MERCATOR: 1,
110
+ /**
111
+ * Render geospatial data as a 3D globe
112
+ */
113
+ GLOBE: 2,
114
+ /**
115
+ * (Internal use only) Web Mercator projection at high zoom
116
+ */
117
+ WEB_MERCATOR_AUTO_OFFSET: 4,
118
+ /**
119
+ * No transformation
120
+ */
121
+ IDENTITY: 0
122
+ };
123
+
124
+ // ../core/src/utils/math-utils.ts
125
+ var import_core = require("@math.gl/core");
126
+ function createMat4() {
127
+ return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
128
+ }
129
+ function mod(value, divisor) {
130
+ const modulus = value % divisor;
131
+ return modulus < 0 ? divisor + modulus : modulus;
132
+ }
133
+ function getCameraPosition(viewMatrixInverse) {
134
+ return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
135
+ }
136
+ function getFrustumPlanes(viewProjectionMatrix) {
137
+ return {
138
+ left: getFrustumPlane(
139
+ viewProjectionMatrix[3] + viewProjectionMatrix[0],
140
+ viewProjectionMatrix[7] + viewProjectionMatrix[4],
141
+ viewProjectionMatrix[11] + viewProjectionMatrix[8],
142
+ viewProjectionMatrix[15] + viewProjectionMatrix[12]
143
+ ),
144
+ right: getFrustumPlane(
145
+ viewProjectionMatrix[3] - viewProjectionMatrix[0],
146
+ viewProjectionMatrix[7] - viewProjectionMatrix[4],
147
+ viewProjectionMatrix[11] - viewProjectionMatrix[8],
148
+ viewProjectionMatrix[15] - viewProjectionMatrix[12]
149
+ ),
150
+ bottom: getFrustumPlane(
151
+ viewProjectionMatrix[3] + viewProjectionMatrix[1],
152
+ viewProjectionMatrix[7] + viewProjectionMatrix[5],
153
+ viewProjectionMatrix[11] + viewProjectionMatrix[9],
154
+ viewProjectionMatrix[15] + viewProjectionMatrix[13]
155
+ ),
156
+ top: getFrustumPlane(
157
+ viewProjectionMatrix[3] - viewProjectionMatrix[1],
158
+ viewProjectionMatrix[7] - viewProjectionMatrix[5],
159
+ viewProjectionMatrix[11] - viewProjectionMatrix[9],
160
+ viewProjectionMatrix[15] - viewProjectionMatrix[13]
161
+ ),
162
+ near: getFrustumPlane(
163
+ viewProjectionMatrix[3] + viewProjectionMatrix[2],
164
+ viewProjectionMatrix[7] + viewProjectionMatrix[6],
165
+ viewProjectionMatrix[11] + viewProjectionMatrix[10],
166
+ viewProjectionMatrix[15] + viewProjectionMatrix[14]
167
+ ),
168
+ far: getFrustumPlane(
169
+ viewProjectionMatrix[3] - viewProjectionMatrix[2],
170
+ viewProjectionMatrix[7] - viewProjectionMatrix[6],
171
+ viewProjectionMatrix[11] - viewProjectionMatrix[10],
172
+ viewProjectionMatrix[15] - viewProjectionMatrix[14]
173
+ )
174
+ };
175
+ }
176
+ var scratchVector = new import_core.Vector3();
177
+ function getFrustumPlane(a, b, c, d) {
178
+ scratchVector.set(a, b, c);
179
+ const L = scratchVector.len();
180
+ return { distance: d / L, normal: new import_core.Vector3(-a / L, -b / L, -c / L) };
181
+ }
182
+
183
+ // ../core/src/viewports/viewport.ts
184
+ var import_core2 = require("@math.gl/core");
185
+ var import_web_mercator = require("@math.gl/web-mercator");
186
+ var DEGREES_TO_RADIANS = Math.PI / 180;
187
+ var IDENTITY = createMat4();
188
+ var ZERO_VECTOR = [0, 0, 0];
189
+ var DEFAULT_DISTANCE_SCALES = {
190
+ unitsPerMeter: [1, 1, 1],
191
+ metersPerUnit: [1, 1, 1]
192
+ };
193
+ function createProjectionMatrix({
194
+ width,
195
+ height,
196
+ orthographic,
197
+ fovyRadians,
198
+ focalDistance,
199
+ padding,
200
+ near,
201
+ far
202
+ }) {
203
+ const aspect = width / height;
204
+ const matrix = orthographic ? new import_core2.Matrix4().orthographic({ fovy: fovyRadians, aspect, focalDistance, near, far }) : new import_core2.Matrix4().perspective({ fovy: fovyRadians, aspect, near, far });
205
+ if (padding) {
206
+ const { left = 0, right = 0, top = 0, bottom = 0 } = padding;
207
+ const offsetX = (0, import_core2.clamp)((left + width - right) / 2, 0, width) - width / 2;
208
+ const offsetY = (0, import_core2.clamp)((top + height - bottom) / 2, 0, height) - height / 2;
209
+ matrix[8] -= offsetX * 2 / width;
210
+ matrix[9] += offsetY * 2 / height;
211
+ }
212
+ return matrix;
213
+ }
214
+ var _Viewport = class _Viewport {
215
+ // eslint-disable-next-line complexity
216
+ constructor(opts = {}) {
217
+ this._frustumPlanes = {};
218
+ this.id = opts.id || this.constructor.displayName || "viewport";
219
+ this.x = opts.x || 0;
220
+ this.y = opts.y || 0;
221
+ this.width = opts.width || 1;
222
+ this.height = opts.height || 1;
223
+ this.zoom = opts.zoom || 0;
224
+ this.padding = opts.padding;
225
+ this.distanceScales = opts.distanceScales || DEFAULT_DISTANCE_SCALES;
226
+ this.focalDistance = opts.focalDistance || 1;
227
+ this.position = opts.position || ZERO_VECTOR;
228
+ this.modelMatrix = opts.modelMatrix || null;
229
+ const { longitude, latitude } = opts;
230
+ this.isGeospatial = Number.isFinite(latitude) && Number.isFinite(longitude);
231
+ this._initProps(opts);
232
+ this._initMatrices(opts);
233
+ this.equals = this.equals.bind(this);
234
+ this.project = this.project.bind(this);
235
+ this.unproject = this.unproject.bind(this);
236
+ this.projectPosition = this.projectPosition.bind(this);
237
+ this.unprojectPosition = this.unprojectPosition.bind(this);
238
+ this.projectFlat = this.projectFlat.bind(this);
239
+ this.unprojectFlat = this.unprojectFlat.bind(this);
240
+ }
241
+ get subViewports() {
242
+ return null;
243
+ }
244
+ get metersPerPixel() {
245
+ return this.distanceScales.metersPerUnit[2] / this.scale;
246
+ }
247
+ get projectionMode() {
248
+ if (this.isGeospatial) {
249
+ return this.zoom < 12 ? PROJECTION_MODE.WEB_MERCATOR : PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET;
250
+ }
251
+ return PROJECTION_MODE.IDENTITY;
252
+ }
253
+ // Two viewports are equal if their dimensions, common-space scale, and
254
+ // view and projection matrices are (approximately) equal.
255
+ equals(viewport) {
256
+ if (!(viewport instanceof _Viewport)) {
257
+ return false;
258
+ }
259
+ if (this === viewport) {
260
+ return true;
261
+ }
262
+ return viewport.width === this.width && viewport.height === this.height && viewport.scale === this.scale && viewport.projectionMode === this.projectionMode && viewport.resolution === this.resolution && (0, import_core2.equals)(viewport.distanceScales.unitsPerMeter, this.distanceScales.unitsPerMeter) && (0, import_core2.equals)(viewport.projectionMatrix, this.projectionMatrix) && (0, import_core2.equals)(viewport.viewMatrix, this.viewMatrix);
263
+ }
264
+ /**
265
+ * Projects xyz (possibly latitude and longitude) to pixel coordinates in window
266
+ * using viewport projection parameters
267
+ * - [longitude, latitude] to [x, y]
268
+ * - [longitude, latitude, Z] => [x, y, z]
269
+ * Note: By default, returns top-left coordinates for canvas/SVG type render
270
+ *
271
+ * @param {Array} lngLatZ - [lng, lat] or [lng, lat, Z]
272
+ * @param {Object} opts - options
273
+ * @param {Object} opts.topLeft=true - Whether projected coords are top left
274
+ * @return {Array} - [x, y] or [x, y, z] in top left coords
275
+ */
276
+ project(xyz, { topLeft = true } = {}) {
277
+ const worldPosition = this.projectPosition(xyz);
278
+ const coord = (0, import_web_mercator.worldToPixels)(worldPosition, this.pixelProjectionMatrix);
279
+ const [x, y] = coord;
280
+ const y2 = topLeft ? y : this.height - y;
281
+ return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]];
282
+ }
283
+ /**
284
+ * Unproject pixel coordinates on screen onto world coordinates,
285
+ * (possibly [lon, lat]) on map.
286
+ * - [x, y] => [lng, lat]
287
+ * - [x, y, z] => [lng, lat, Z]
288
+ * @param {Array} xyz -
289
+ * @param {Object} opts - options
290
+ * @param {Object} opts.topLeft=true - Whether origin is top left
291
+ * @return {Array|null} - [lng, lat, Z] or [X, Y, Z]
292
+ */
293
+ unproject(xyz, { topLeft = true, targetZ } = {}) {
294
+ const [x, y, z] = xyz;
295
+ const y2 = topLeft ? y : this.height - y;
296
+ const targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2];
297
+ const coord = (0, import_web_mercator.pixelsToWorld)([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld);
298
+ const [X, Y, Z] = this.unprojectPosition(coord);
299
+ if (Number.isFinite(z)) {
300
+ return [X, Y, Z];
301
+ }
302
+ return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
303
+ }
304
+ // NON_LINEAR PROJECTION HOOKS
305
+ // Used for web meractor projection
306
+ projectPosition(xyz) {
307
+ const [X, Y] = this.projectFlat(xyz);
308
+ const Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2];
309
+ return [X, Y, Z];
310
+ }
311
+ unprojectPosition(xyz) {
312
+ const [X, Y] = this.unprojectFlat(xyz);
313
+ const Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2];
314
+ return [X, Y, Z];
315
+ }
316
+ /**
317
+ * Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
318
+ * Performs the nonlinear part of the web mercator projection.
319
+ * Remaining projection is done with 4x4 matrices which also handles
320
+ * perspective.
321
+ * @param {Array} lngLat - [lng, lat] coordinates
322
+ * Specifies a point on the sphere to project onto the map.
323
+ * @return {Array} [x,y] coordinates.
324
+ */
325
+ projectFlat(xyz) {
326
+ if (this.isGeospatial) {
327
+ const result = (0, import_web_mercator.lngLatToWorld)(xyz);
328
+ result[1] = (0, import_core2.clamp)(result[1], -318, 830);
329
+ return result;
330
+ }
331
+ return xyz;
332
+ }
333
+ /**
334
+ * Unproject world point [x,y] on map onto {lat, lon} on sphere
335
+ * @param {object|Vector} xy - object with {x,y} members
336
+ * representing point on projected map plane
337
+ * @return {GeoCoordinates} - object with {lat,lon} of point on sphere.
338
+ * Has toArray method if you need a GeoJSON Array.
339
+ * Per cartographic tradition, lat and lon are specified as degrees.
340
+ */
341
+ unprojectFlat(xyz) {
342
+ if (this.isGeospatial) {
343
+ return (0, import_web_mercator.worldToLngLat)(xyz);
344
+ }
345
+ return xyz;
346
+ }
347
+ /**
348
+ * Get bounds of the current viewport
349
+ * @return {Array} - [minX, minY, maxX, maxY]
350
+ */
351
+ getBounds(options = {}) {
352
+ const unprojectOption = { targetZ: options.z || 0 };
353
+ const topLeft = this.unproject([0, 0], unprojectOption);
354
+ const topRight = this.unproject([this.width, 0], unprojectOption);
355
+ const bottomLeft = this.unproject([0, this.height], unprojectOption);
356
+ const bottomRight = this.unproject([this.width, this.height], unprojectOption);
357
+ return [
358
+ Math.min(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
359
+ Math.min(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]),
360
+ Math.max(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
361
+ Math.max(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1])
362
+ ];
363
+ }
364
+ getDistanceScales(coordinateOrigin) {
365
+ if (coordinateOrigin && this.isGeospatial) {
366
+ return (0, import_web_mercator.getDistanceScales)({
367
+ longitude: coordinateOrigin[0],
368
+ latitude: coordinateOrigin[1],
369
+ highPrecision: true
370
+ });
371
+ }
372
+ return this.distanceScales;
373
+ }
374
+ containsPixel({
375
+ x,
376
+ y,
377
+ width = 1,
378
+ height = 1
379
+ }) {
380
+ return x < this.x + this.width && this.x < x + width && y < this.y + this.height && this.y < y + height;
381
+ }
382
+ // Extract frustum planes in common space
383
+ getFrustumPlanes() {
384
+ if (this._frustumPlanes.near) {
385
+ return this._frustumPlanes;
386
+ }
387
+ Object.assign(this._frustumPlanes, getFrustumPlanes(this.viewProjectionMatrix));
388
+ return this._frustumPlanes;
389
+ }
390
+ // EXPERIMENTAL METHODS
391
+ /**
392
+ * Needed by panning and linear transition
393
+ * Pan the viewport to place a given world coordinate at screen point [x, y]
394
+ *
395
+ * @param {Array} coords - world coordinates
396
+ * @param {Array} pixel - [x,y] coordinates on screen
397
+ * @param {Array} startPixel - [x,y] screen position where pan started (optional, for delta-based panning)
398
+ * @return {Object} props of the new viewport
399
+ */
400
+ panByPosition(coords, pixel, startPixel) {
401
+ return null;
402
+ }
403
+ // INTERNAL METHODS
404
+ /* eslint-disable complexity, max-statements */
405
+ _initProps(opts) {
406
+ const longitude = opts.longitude;
407
+ const latitude = opts.latitude;
408
+ if (this.isGeospatial) {
409
+ if (!Number.isFinite(opts.zoom)) {
410
+ this.zoom = (0, import_web_mercator.getMeterZoom)({ latitude }) + Math.log2(this.focalDistance);
411
+ }
412
+ this.distanceScales = opts.distanceScales || (0, import_web_mercator.getDistanceScales)({ latitude, longitude });
413
+ }
414
+ const scale = Math.pow(2, this.zoom);
415
+ this.scale = scale;
416
+ const { position, modelMatrix } = opts;
417
+ let meterOffset = ZERO_VECTOR;
418
+ if (position) {
419
+ meterOffset = modelMatrix ? new import_core2.Matrix4(modelMatrix).transformAsVector(position, []) : position;
420
+ }
421
+ if (this.isGeospatial) {
422
+ const center = this.projectPosition([longitude, latitude, 0]);
423
+ this.center = new import_core2.Vector3(meterOffset).scale(this.distanceScales.unitsPerMeter).add(center);
424
+ } else {
425
+ this.center = this.projectPosition(meterOffset);
426
+ }
427
+ }
428
+ /* eslint-enable complexity, max-statements */
429
+ _initMatrices(opts) {
430
+ const {
431
+ // View matrix
432
+ viewMatrix: viewMatrix2 = IDENTITY,
433
+ // Projection matrix
434
+ projectionMatrix = null,
435
+ // Projection matrix parameters, used if projectionMatrix not supplied
436
+ orthographic = false,
437
+ fovyRadians,
438
+ fovy = 75,
439
+ near = 0.1,
440
+ // Distance of near clipping plane
441
+ far = 1e3,
442
+ // Distance of far clipping plane
443
+ padding = null,
444
+ // Center offset in pixels
445
+ focalDistance = 1
446
+ } = opts;
447
+ this.viewMatrixUncentered = viewMatrix2;
448
+ this.viewMatrix = new import_core2.Matrix4().multiplyRight(viewMatrix2).translate(new import_core2.Vector3(this.center).negate());
449
+ this.projectionMatrix = projectionMatrix || createProjectionMatrix({
450
+ width: this.width,
451
+ height: this.height,
452
+ orthographic,
453
+ fovyRadians: fovyRadians || fovy * DEGREES_TO_RADIANS,
454
+ focalDistance,
455
+ padding,
456
+ near,
457
+ far
458
+ });
459
+ const vpm = createMat4();
460
+ import_core2.mat4.multiply(vpm, vpm, this.projectionMatrix);
461
+ import_core2.mat4.multiply(vpm, vpm, this.viewMatrix);
462
+ this.viewProjectionMatrix = vpm;
463
+ this.viewMatrixInverse = import_core2.mat4.invert([], this.viewMatrix) || this.viewMatrix;
464
+ this.cameraPosition = getCameraPosition(this.viewMatrixInverse);
465
+ const viewportMatrix = createMat4();
466
+ const pixelProjectionMatrix = createMat4();
467
+ import_core2.mat4.scale(viewportMatrix, viewportMatrix, [this.width / 2, -this.height / 2, 1]);
468
+ import_core2.mat4.translate(viewportMatrix, viewportMatrix, [1, -1, 0]);
469
+ import_core2.mat4.multiply(pixelProjectionMatrix, viewportMatrix, this.viewProjectionMatrix);
470
+ this.pixelProjectionMatrix = pixelProjectionMatrix;
471
+ this.pixelUnprojectionMatrix = import_core2.mat4.invert(createMat4(), this.pixelProjectionMatrix);
472
+ if (!this.pixelUnprojectionMatrix) {
473
+ log_default.warn("Pixel project matrix not invertible")();
474
+ }
475
+ }
476
+ };
477
+ _Viewport.displayName = "Viewport";
478
+ var Viewport = _Viewport;
479
+
480
+ // ../core/src/viewports/web-mercator-viewport.ts
481
+ var import_web_mercator2 = require("@math.gl/web-mercator");
482
+ var import_core3 = require("@math.gl/core");
483
+ var _WebMercatorViewport = class _WebMercatorViewport extends Viewport {
484
+ /* eslint-disable complexity, max-statements */
485
+ constructor(opts = {}) {
486
+ const {
487
+ latitude = 0,
488
+ longitude = 0,
489
+ zoom = 0,
490
+ pitch = 0,
491
+ bearing = 0,
492
+ nearZMultiplier = 0.1,
493
+ farZMultiplier = 1.01,
494
+ nearZ,
495
+ farZ,
496
+ orthographic = false,
497
+ projectionMatrix,
498
+ repeat = false,
499
+ worldOffset = 0,
500
+ position,
501
+ padding,
502
+ // backward compatibility
503
+ // TODO: remove in v9
504
+ legacyMeterSizes = false
505
+ } = opts;
506
+ let { width, height, altitude = 1.5 } = opts;
507
+ const scale = Math.pow(2, zoom);
508
+ width = width || 1;
509
+ height = height || 1;
510
+ let fovy;
511
+ let projectionParameters = null;
512
+ if (projectionMatrix) {
513
+ altitude = projectionMatrix[5] / 2;
514
+ fovy = (0, import_web_mercator2.altitudeToFovy)(altitude);
515
+ } else {
516
+ if (opts.fovy) {
517
+ fovy = opts.fovy;
518
+ altitude = (0, import_web_mercator2.fovyToAltitude)(fovy);
519
+ } else {
520
+ fovy = (0, import_web_mercator2.altitudeToFovy)(altitude);
521
+ }
522
+ let offset3;
523
+ if (padding) {
524
+ const { top = 0, bottom = 0 } = padding;
525
+ offset3 = [0, (0, import_core3.clamp)((top + height - bottom) / 2, 0, height) - height / 2];
526
+ }
527
+ projectionParameters = (0, import_web_mercator2.getProjectionParameters)({
528
+ width,
529
+ height,
530
+ scale,
531
+ center: position && [0, 0, position[2] * (0, import_web_mercator2.unitsPerMeter)(latitude)],
532
+ offset: offset3,
533
+ pitch,
534
+ fovy,
535
+ nearZMultiplier,
536
+ farZMultiplier
537
+ });
538
+ if (Number.isFinite(nearZ)) {
539
+ projectionParameters.near = nearZ;
540
+ }
541
+ if (Number.isFinite(farZ)) {
542
+ projectionParameters.far = farZ;
543
+ }
544
+ }
545
+ let viewMatrixUncentered = (0, import_web_mercator2.getViewMatrix)({
546
+ height,
547
+ pitch,
548
+ bearing,
549
+ scale,
550
+ altitude
551
+ });
552
+ if (worldOffset) {
553
+ const viewOffset = new import_core3.Matrix4().translate([512 * worldOffset, 0, 0]);
554
+ viewMatrixUncentered = viewOffset.multiplyLeft(viewMatrixUncentered);
555
+ }
556
+ super({
557
+ ...opts,
558
+ // x, y,
559
+ width,
560
+ height,
561
+ // view matrix
562
+ viewMatrix: viewMatrixUncentered,
563
+ longitude,
564
+ latitude,
565
+ zoom,
566
+ // projection matrix parameters
567
+ ...projectionParameters,
568
+ fovy,
569
+ focalDistance: altitude
570
+ });
571
+ this.latitude = latitude;
572
+ this.longitude = longitude;
573
+ this.zoom = zoom;
574
+ this.pitch = pitch;
575
+ this.bearing = bearing;
576
+ this.altitude = altitude;
577
+ this.fovy = fovy;
578
+ this.orthographic = orthographic;
579
+ this._subViewports = repeat ? [] : null;
580
+ this._pseudoMeters = legacyMeterSizes;
581
+ Object.freeze(this);
582
+ }
583
+ /* eslint-enable complexity, max-statements */
584
+ get subViewports() {
585
+ if (this._subViewports && !this._subViewports.length) {
586
+ const bounds = this.getBounds();
587
+ const minOffset = Math.floor((bounds[0] + 180) / 360);
588
+ const maxOffset = Math.ceil((bounds[2] - 180) / 360);
589
+ for (let x = minOffset; x <= maxOffset; x++) {
590
+ const offsetViewport = x ? new _WebMercatorViewport({
591
+ ...this,
592
+ worldOffset: x
593
+ }) : this;
594
+ this._subViewports.push(offsetViewport);
595
+ }
596
+ }
597
+ return this._subViewports;
598
+ }
599
+ /** Returns whether two Web Mercator viewports use the same projection settings. */
600
+ equals(viewport) {
601
+ return viewport instanceof _WebMercatorViewport && viewport._pseudoMeters === this._pseudoMeters && super.equals(viewport);
602
+ }
603
+ projectPosition(xyz) {
604
+ if (this._pseudoMeters) {
605
+ return super.projectPosition(xyz);
606
+ }
607
+ const [X, Y] = this.projectFlat(xyz);
608
+ const Z = (xyz[2] || 0) * (0, import_web_mercator2.unitsPerMeter)(xyz[1]);
609
+ return [X, Y, Z];
610
+ }
611
+ unprojectPosition(xyz) {
612
+ if (this._pseudoMeters) {
613
+ return super.unprojectPosition(xyz);
614
+ }
615
+ const [X, Y] = this.unprojectFlat(xyz);
616
+ const Z = (xyz[2] || 0) / (0, import_web_mercator2.unitsPerMeter)(Y);
617
+ return [X, Y, Z];
618
+ }
619
+ /**
620
+ * Add a meter delta to a base lnglat coordinate, returning a new lnglat array
621
+ *
622
+ * Note: Uses simple linear approximation around the viewport center
623
+ * Error increases with size of offset (roughly 1% per 100km)
624
+ *
625
+ * @param {[Number,Number]|[Number,Number,Number]) lngLatZ - base coordinate
626
+ * @param {[Number,Number]|[Number,Number,Number]) xyz - array of meter deltas
627
+ * @return {[Number,Number]|[Number,Number,Number]) array of [lng,lat,z] deltas
628
+ */
629
+ addMetersToLngLat(lngLatZ, xyz) {
630
+ return (0, import_web_mercator2.addMetersToLngLat)(lngLatZ, xyz);
631
+ }
632
+ panByPosition(coords, pixel, startPixel) {
633
+ const fromLocation = (0, import_web_mercator2.pixelsToWorld)(pixel, this.pixelUnprojectionMatrix);
634
+ const toLocation = this.projectFlat(coords);
635
+ const translate = import_core3.vec2.add([], toLocation, import_core3.vec2.negate([], fromLocation));
636
+ const newCenter = import_core3.vec2.add([], this.center, translate);
637
+ const [longitude, latitude] = this.unprojectFlat(newCenter);
638
+ return { longitude, latitude };
639
+ }
640
+ /**
641
+ * Returns a new longitude and latitude that keeps a 3D world coordinate at a given screen pixel
642
+ * This version handles the z-component (altitude) properly for cameras positioned above ground
643
+ */
644
+ panByPosition3D(coords, pixel) {
645
+ const targetZ = coords[2] || 0;
646
+ const deltaLngLat = import_core3.vec2.sub([], coords, this.unproject(pixel, { targetZ }));
647
+ return { longitude: this.longitude + deltaLngLat[0], latitude: this.latitude + deltaLngLat[1] };
648
+ }
649
+ getBounds(options = {}) {
650
+ const corners = (0, import_web_mercator2.getBounds)(this, options.z || 0);
651
+ return [
652
+ Math.min(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
653
+ Math.min(corners[0][1], corners[1][1], corners[2][1], corners[3][1]),
654
+ Math.max(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
655
+ Math.max(corners[0][1], corners[1][1], corners[2][1], corners[3][1])
656
+ ];
657
+ }
658
+ /**
659
+ * Returns a new viewport that fit around the given rectangle.
660
+ * Only supports non-perspective mode.
661
+ */
662
+ fitBounds(bounds, options = {}) {
663
+ const { width, height } = this;
664
+ const { longitude, latitude, zoom } = (0, import_web_mercator2.fitBounds)({ width, height, bounds, ...options });
665
+ return new _WebMercatorViewport({ width, height, longitude, latitude, zoom });
666
+ }
667
+ };
668
+ _WebMercatorViewport.displayName = "WebMercatorViewport";
669
+ var WebMercatorViewport = _WebMercatorViewport;
670
+
671
+ // ../core/src/utils/deep-equal.ts
672
+ function deepEqual(a, b, depth) {
673
+ if (a === b) {
674
+ return true;
675
+ }
676
+ if (!depth || !a || !b) {
677
+ return false;
678
+ }
679
+ if (Array.isArray(a)) {
680
+ if (!Array.isArray(b) || a.length !== b.length) {
681
+ return false;
682
+ }
683
+ for (let i = 0; i < a.length; i++) {
684
+ if (!deepEqual(a[i], b[i], depth - 1)) {
685
+ return false;
686
+ }
687
+ }
688
+ return true;
689
+ }
690
+ if (Array.isArray(b)) {
691
+ return false;
692
+ }
693
+ if (typeof a === "object" && typeof b === "object") {
694
+ const aKeys = Object.keys(a);
695
+ const bKeys = Object.keys(b);
696
+ if (aKeys.length !== bKeys.length) {
697
+ return false;
698
+ }
699
+ for (const key of aKeys) {
700
+ if (!b.hasOwnProperty(key)) {
701
+ return false;
702
+ }
703
+ if (!deepEqual(a[key], b[key], depth - 1)) {
704
+ return false;
705
+ }
706
+ }
707
+ return true;
708
+ }
709
+ return false;
710
+ }
711
+
712
+ // ../core/src/utils/positions.ts
713
+ var NUMBER_REGEX = /^(?:\d+\.?\d*|\.\d+)$/;
714
+ function parsePosition(value) {
715
+ switch (typeof value) {
716
+ case "number":
717
+ if (!Number.isFinite(value)) {
718
+ throw new Error(`Could not parse position string ${value}`);
719
+ }
720
+ return { type: "literal", value };
721
+ case "string":
722
+ try {
723
+ const tokens = tokenize(value);
724
+ const parser = new LayoutExpressionParser(tokens);
725
+ return parser.parseExpression();
726
+ } catch (error) {
727
+ const reason = error instanceof Error ? error.message : String(error);
728
+ throw new Error(`Could not parse position string ${value}: ${reason}`);
729
+ }
730
+ default:
731
+ throw new Error(`Could not parse position string ${value}`);
732
+ }
733
+ }
734
+ function evaluateLayoutExpression(expression, extent) {
735
+ switch (expression.type) {
736
+ case "literal":
737
+ return expression.value;
738
+ case "percentage":
739
+ return Math.round(expression.value * extent);
740
+ case "binary":
741
+ const left = evaluateLayoutExpression(expression.left, extent);
742
+ const right = evaluateLayoutExpression(expression.right, extent);
743
+ return expression.operator === "+" ? left + right : left - right;
744
+ default:
745
+ throw new Error("Unknown layout expression type");
746
+ }
747
+ }
748
+ function getPosition(expression, extent) {
749
+ return evaluateLayoutExpression(expression, extent);
750
+ }
751
+ function tokenize(input) {
752
+ const tokens = [];
753
+ let index = 0;
754
+ while (index < input.length) {
755
+ const char = input[index];
756
+ if (/\s/.test(char)) {
757
+ index++;
758
+ continue;
759
+ }
760
+ if (char === "+" || char === "-" || char === "(" || char === ")" || char === "%") {
761
+ tokens.push({ type: "symbol", value: char });
762
+ index++;
763
+ continue;
764
+ }
765
+ if (isDigit(char) || char === ".") {
766
+ const start = index;
767
+ let hasDecimal = char === ".";
768
+ index++;
769
+ while (index < input.length) {
770
+ const next = input[index];
771
+ if (isDigit(next)) {
772
+ index++;
773
+ continue;
774
+ }
775
+ if (next === "." && !hasDecimal) {
776
+ hasDecimal = true;
777
+ index++;
778
+ continue;
779
+ }
780
+ break;
781
+ }
782
+ const numberString = input.slice(start, index);
783
+ if (!NUMBER_REGEX.test(numberString)) {
784
+ throw new Error("Invalid number token");
785
+ }
786
+ tokens.push({ type: "number", value: parseFloat(numberString) });
787
+ continue;
788
+ }
789
+ if (isAlpha(char)) {
790
+ const start = index;
791
+ while (index < input.length && isAlpha(input[index])) {
792
+ index++;
793
+ }
794
+ const word = input.slice(start, index).toLowerCase();
795
+ tokens.push({ type: "word", value: word });
796
+ continue;
797
+ }
798
+ throw new Error("Invalid token in position string");
799
+ }
800
+ return tokens;
801
+ }
802
+ var LayoutExpressionParser = class {
803
+ constructor(tokens) {
804
+ this.index = 0;
805
+ this.tokens = tokens;
806
+ }
807
+ parseExpression() {
808
+ const expression = this.parseBinaryExpression();
809
+ if (this.index < this.tokens.length) {
810
+ throw new Error("Unexpected token at end of expression");
811
+ }
812
+ return expression;
813
+ }
814
+ parseBinaryExpression() {
815
+ let expression = this.parseFactor();
816
+ let token = this.peek();
817
+ while (isAddSubSymbol(token)) {
818
+ this.index++;
819
+ const right = this.parseFactor();
820
+ expression = { type: "binary", operator: token.value, left: expression, right };
821
+ token = this.peek();
822
+ }
823
+ return expression;
824
+ }
825
+ parseFactor() {
826
+ const token = this.peek();
827
+ if (!token) {
828
+ throw new Error("Unexpected end of expression");
829
+ }
830
+ if (token.type === "symbol" && token.value === "+") {
831
+ this.index++;
832
+ return this.parseFactor();
833
+ }
834
+ if (token.type === "symbol" && token.value === "-") {
835
+ this.index++;
836
+ const factor = this.parseFactor();
837
+ return { type: "binary", operator: "-", left: { type: "literal", value: 0 }, right: factor };
838
+ }
839
+ if (token.type === "symbol" && token.value === "(") {
840
+ this.index++;
841
+ const expression = this.parseBinaryExpression();
842
+ if (!this.consumeSymbol(")")) {
843
+ throw new Error("Missing closing parenthesis");
844
+ }
845
+ return expression;
846
+ }
847
+ if (token.type === "word" && token.value === "calc") {
848
+ this.index++;
849
+ if (!this.consumeSymbol("(")) {
850
+ throw new Error("Missing opening parenthesis after calc");
851
+ }
852
+ const expression = this.parseBinaryExpression();
853
+ if (!this.consumeSymbol(")")) {
854
+ throw new Error("Missing closing parenthesis");
855
+ }
856
+ return expression;
857
+ }
858
+ if (token.type === "number") {
859
+ this.index++;
860
+ const numberValue = token.value;
861
+ const nextToken = this.peek();
862
+ if (nextToken && nextToken.type === "symbol" && nextToken.value === "%") {
863
+ this.index++;
864
+ return { type: "percentage", value: numberValue / 100 };
865
+ }
866
+ if (nextToken && nextToken.type === "word" && nextToken.value === "px") {
867
+ this.index++;
868
+ return { type: "literal", value: numberValue };
869
+ }
870
+ return { type: "literal", value: numberValue };
871
+ }
872
+ throw new Error("Unexpected token in expression");
873
+ }
874
+ consumeSymbol(value) {
875
+ const token = this.peek();
876
+ if (token && token.type === "symbol" && token.value === value) {
877
+ this.index++;
878
+ return true;
879
+ }
880
+ return false;
881
+ }
882
+ peek() {
883
+ return this.tokens[this.index] || null;
884
+ }
885
+ };
886
+ function isDigit(char) {
887
+ return char >= "0" && char <= "9";
888
+ }
889
+ function isAlpha(char) {
890
+ return char >= "a" && char <= "z" || char >= "A" && char <= "Z";
891
+ }
892
+ function isAddSubSymbol(token) {
893
+ return Boolean(token && token.type === "symbol" && (token.value === "+" || token.value === "-"));
894
+ }
895
+
896
+ // ../core/src/utils/deep-merge.ts
897
+ function deepMergeViewState(a, b) {
898
+ const result = { ...a };
899
+ for (const key in b) {
900
+ if (key === "id") continue;
901
+ if (Array.isArray(result[key]) && Array.isArray(b[key])) {
902
+ result[key] = mergeNumericArray(result[key], b[key]);
903
+ } else {
904
+ result[key] = b[key];
905
+ }
906
+ }
907
+ return result;
908
+ }
909
+ function mergeNumericArray(target, source) {
910
+ target = target.slice();
911
+ for (let i = 0; i < source.length; i++) {
912
+ const v = source[i];
913
+ if (Number.isFinite(v)) {
914
+ target[i] = v;
915
+ }
916
+ }
917
+ return target;
918
+ }
919
+
920
+ // ../core/src/views/view.ts
921
+ var View = class {
922
+ constructor(props) {
923
+ const { id, x = 0, y = 0, width = "100%", height = "100%", padding = null } = props;
924
+ this.id = id || this.constructor.displayName || "view";
925
+ this.props = { ...props, id: this.id };
926
+ this._x = parsePosition(x);
927
+ this._y = parsePosition(y);
928
+ this._width = parsePosition(width);
929
+ this._height = parsePosition(height);
930
+ this._padding = padding && {
931
+ left: parsePosition(padding.left || 0),
932
+ right: parsePosition(padding.right || 0),
933
+ top: parsePosition(padding.top || 0),
934
+ bottom: parsePosition(padding.bottom || 0)
935
+ };
936
+ this.equals = this.equals.bind(this);
937
+ Object.seal(this);
938
+ }
939
+ equals(view) {
940
+ if (this === view) {
941
+ return true;
942
+ }
943
+ return this.constructor === view.constructor && deepEqual(this.props, view.props, 2);
944
+ }
945
+ /** Clone this view with modified props */
946
+ clone(newProps) {
947
+ const ViewConstructor = this.constructor;
948
+ return new ViewConstructor({ ...this.props, ...newProps });
949
+ }
950
+ /** Make viewport from canvas dimensions and view state */
951
+ makeViewport({ width, height, viewState }) {
952
+ viewState = this.filterViewState(viewState);
953
+ const viewportDimensions = this.getDimensions({ width, height });
954
+ if (!viewportDimensions.height || !viewportDimensions.width) {
955
+ return null;
956
+ }
957
+ const ViewportType = this.getViewportType(viewState);
958
+ return new ViewportType({ ...viewState, ...this.props, ...viewportDimensions });
959
+ }
960
+ getViewStateId() {
961
+ const { viewState } = this.props;
962
+ if (typeof viewState === "string") {
963
+ return viewState;
964
+ }
965
+ return (viewState == null ? void 0 : viewState.id) || this.id;
966
+ }
967
+ // Allows view to override (or completely define) viewState
968
+ filterViewState(viewState) {
969
+ if (this.props.viewState && typeof this.props.viewState === "object") {
970
+ if (!this.props.viewState.id) {
971
+ return this.props.viewState;
972
+ }
973
+ return deepMergeViewState(viewState, this.props.viewState);
974
+ }
975
+ return viewState;
976
+ }
977
+ /** Resolve the dimensions of the view from overall canvas dimensions */
978
+ getDimensions({ width, height }) {
979
+ const dimensions = {
980
+ x: getPosition(this._x, width),
981
+ y: getPosition(this._y, height),
982
+ width: getPosition(this._width, width),
983
+ height: getPosition(this._height, height)
984
+ };
985
+ if (this._padding) {
986
+ dimensions.padding = {
987
+ left: getPosition(this._padding.left, width),
988
+ top: getPosition(this._padding.top, height),
989
+ right: getPosition(this._padding.right, width),
990
+ bottom: getPosition(this._padding.bottom, height)
991
+ };
992
+ }
993
+ return dimensions;
994
+ }
995
+ // Used by sub classes to resolve controller props
996
+ get controller() {
997
+ const opts = this.props.controller;
998
+ if (!opts) {
999
+ return null;
1000
+ }
1001
+ if (opts === true) {
1002
+ return { type: this.ControllerType };
1003
+ }
1004
+ if (typeof opts === "function") {
1005
+ return { type: opts };
1006
+ }
1007
+ return { type: this.ControllerType, ...opts };
1008
+ }
1009
+ };
1010
+
1011
+ // ../core/src/transitions/transition.ts
1012
+ var Transition = class {
1013
+ /**
1014
+ * @params timeline {Timeline}
1015
+ */
1016
+ constructor(timeline) {
1017
+ this._inProgress = false;
1018
+ this._handle = null;
1019
+ this.time = 0;
1020
+ // @ts-expect-error
1021
+ this.settings = {
1022
+ duration: 0
1023
+ };
1024
+ this._timeline = timeline;
1025
+ }
1026
+ /* Public API */
1027
+ get inProgress() {
1028
+ return this._inProgress;
1029
+ }
1030
+ /**
1031
+ * (re)start this transition.
1032
+ * @params props {object} - optional overriding props. see constructor
1033
+ */
1034
+ start(settings) {
1035
+ var _a, _b;
1036
+ this.cancel();
1037
+ this.settings = settings;
1038
+ this._inProgress = true;
1039
+ (_b = (_a = this.settings).onStart) == null ? void 0 : _b.call(_a, this);
1040
+ }
1041
+ /**
1042
+ * end this transition if it is in progress.
1043
+ */
1044
+ end() {
1045
+ var _a, _b;
1046
+ if (this._inProgress) {
1047
+ this._timeline.removeChannel(this._handle);
1048
+ this._handle = null;
1049
+ this._inProgress = false;
1050
+ (_b = (_a = this.settings).onEnd) == null ? void 0 : _b.call(_a, this);
1051
+ }
1052
+ }
1053
+ /**
1054
+ * cancel this transition if it is in progress.
1055
+ */
1056
+ cancel() {
1057
+ var _a, _b;
1058
+ if (this._inProgress) {
1059
+ (_b = (_a = this.settings).onInterrupt) == null ? void 0 : _b.call(_a, this);
1060
+ this._timeline.removeChannel(this._handle);
1061
+ this._handle = null;
1062
+ this._inProgress = false;
1063
+ }
1064
+ }
1065
+ /**
1066
+ * update this transition. Returns `true` if updated.
1067
+ */
1068
+ update() {
1069
+ var _a, _b;
1070
+ if (!this._inProgress) {
1071
+ return false;
1072
+ }
1073
+ if (this._handle === null) {
1074
+ const { _timeline: timeline, settings } = this;
1075
+ this._handle = timeline.addChannel({
1076
+ delay: timeline.getTime(),
1077
+ duration: settings.duration
1078
+ });
1079
+ }
1080
+ this.time = this._timeline.getTime(this._handle);
1081
+ this._onUpdate();
1082
+ (_b = (_a = this.settings).onUpdate) == null ? void 0 : _b.call(_a, this);
1083
+ if (this._timeline.isFinished(this._handle)) {
1084
+ this.end();
1085
+ }
1086
+ return true;
1087
+ }
1088
+ /* Private API */
1089
+ _onUpdate() {
1090
+ }
1091
+ };
1092
+
1093
+ // ../core/src/controllers/transition-manager.ts
1094
+ var noop = () => {
1095
+ };
1096
+ var PRESERVE_CONSTRAINT_CONTEXT = { mode: "preserve" };
1097
+ var HARD_CONSTRAINT_CONTEXT = { mode: "hard" };
1098
+ var TRANSITION_EVENTS = {
1099
+ BREAK: 1,
1100
+ SNAP_TO_END: 2,
1101
+ IGNORE: 3
1102
+ };
1103
+ var DEFAULT_EASING = (t) => t;
1104
+ var DEFAULT_INTERRUPTION = TRANSITION_EVENTS.BREAK;
1105
+ var TransitionManager = class {
1106
+ constructor(opts) {
1107
+ this._onTransitionUpdate = (transition) => {
1108
+ const {
1109
+ time,
1110
+ settings: { interpolator, startProps, endProps, duration, easing }
1111
+ } = transition;
1112
+ const t = easing(time / duration);
1113
+ const viewport = interpolator.interpolateProps(startProps, endProps, t);
1114
+ this.propsInTransition = this.getControllerState(
1115
+ {
1116
+ ...this.props,
1117
+ ...viewport
1118
+ },
1119
+ PRESERVE_CONSTRAINT_CONTEXT
1120
+ ).getViewportProps();
1121
+ this.onViewStateChange({
1122
+ viewState: this.propsInTransition,
1123
+ oldViewState: this.props
1124
+ });
1125
+ };
1126
+ this.getControllerState = opts.getControllerState;
1127
+ this.propsInTransition = null;
1128
+ this.transition = new Transition(opts.timeline);
1129
+ this.onViewStateChange = opts.onViewStateChange || noop;
1130
+ this.onStateChange = opts.onStateChange || noop;
1131
+ }
1132
+ finalize() {
1133
+ this.transition.cancel();
1134
+ }
1135
+ // Returns current transitioned viewport.
1136
+ getViewportInTransition() {
1137
+ return this.propsInTransition;
1138
+ }
1139
+ // Process the vewiport change, either ignore or trigger a new transition.
1140
+ // Return true if a new transition is triggered, false otherwise.
1141
+ processViewStateChange(nextProps) {
1142
+ let transitionTriggered = false;
1143
+ const currentProps = this.props;
1144
+ this.props = nextProps;
1145
+ if (!currentProps || this._shouldIgnoreViewportChange(currentProps, nextProps)) {
1146
+ return false;
1147
+ }
1148
+ if (this._isTransitionEnabled(nextProps)) {
1149
+ let startProps = currentProps;
1150
+ if (this.transition.inProgress) {
1151
+ const { interruption, endProps } = this.transition.settings;
1152
+ startProps = {
1153
+ ...currentProps,
1154
+ ...interruption === TRANSITION_EVENTS.SNAP_TO_END ? endProps : this.propsInTransition || currentProps
1155
+ };
1156
+ }
1157
+ this._triggerTransition(startProps, nextProps);
1158
+ transitionTriggered = true;
1159
+ } else {
1160
+ this.transition.cancel();
1161
+ }
1162
+ return transitionTriggered;
1163
+ }
1164
+ updateTransition() {
1165
+ this.transition.update();
1166
+ }
1167
+ // Helper methods
1168
+ _isTransitionEnabled(props) {
1169
+ const { transitionDuration, transitionInterpolator } = props;
1170
+ return (transitionDuration > 0 || transitionDuration === "auto") && Boolean(transitionInterpolator);
1171
+ }
1172
+ _isUpdateDueToCurrentTransition(props) {
1173
+ if (this.transition.inProgress && this.propsInTransition) {
1174
+ return this.transition.settings.interpolator.arePropsEqual(
1175
+ props,
1176
+ this.propsInTransition
1177
+ );
1178
+ }
1179
+ return false;
1180
+ }
1181
+ _shouldIgnoreViewportChange(currentProps, nextProps) {
1182
+ if (this.transition.inProgress) {
1183
+ const transitionSettings = this.transition.settings;
1184
+ return transitionSettings.interruption === TRANSITION_EVENTS.IGNORE || // Ignore update if it is due to current active transition.
1185
+ this._isUpdateDueToCurrentTransition(nextProps);
1186
+ }
1187
+ if (this._isTransitionEnabled(nextProps)) {
1188
+ return nextProps.transitionInterpolator.arePropsEqual(
1189
+ currentProps,
1190
+ nextProps
1191
+ );
1192
+ }
1193
+ return true;
1194
+ }
1195
+ _triggerTransition(startProps, endProps) {
1196
+ const startViewstate = this.getControllerState(startProps, PRESERVE_CONSTRAINT_CONTEXT);
1197
+ const endViewStateProps = this.getControllerState(
1198
+ endProps,
1199
+ HARD_CONSTRAINT_CONTEXT
1200
+ ).shortestPathFrom(startViewstate);
1201
+ const transitionInterpolator = endProps.transitionInterpolator;
1202
+ const duration = transitionInterpolator.getDuration ? transitionInterpolator.getDuration(startProps, endProps) : endProps.transitionDuration;
1203
+ if (duration === 0) {
1204
+ return;
1205
+ }
1206
+ const initialProps = transitionInterpolator.initializeProps(startProps, endViewStateProps);
1207
+ this.propsInTransition = {};
1208
+ const transitionSettings = {
1209
+ duration,
1210
+ easing: endProps.transitionEasing || DEFAULT_EASING,
1211
+ interpolator: transitionInterpolator,
1212
+ interruption: endProps.transitionInterruption || DEFAULT_INTERRUPTION,
1213
+ startProps: initialProps.start,
1214
+ endProps: initialProps.end,
1215
+ onStart: endProps.onTransitionStart,
1216
+ onUpdate: this._onTransitionUpdate,
1217
+ onInterrupt: this._onTransitionEnd(endProps.onTransitionInterrupt),
1218
+ onEnd: this._onTransitionEnd(endProps.onTransitionEnd)
1219
+ };
1220
+ this.transition.start(transitionSettings);
1221
+ this.onStateChange({ inTransition: true });
1222
+ this.updateTransition();
1223
+ }
1224
+ _onTransitionEnd(callback) {
1225
+ return (transition) => {
1226
+ this.propsInTransition = null;
1227
+ this.onStateChange({
1228
+ inTransition: false,
1229
+ isZooming: false,
1230
+ isPanning: false,
1231
+ isRotating: false
1232
+ });
1233
+ callback == null ? void 0 : callback(transition);
1234
+ };
1235
+ }
1236
+ };
1237
+
1238
+ // ../core/src/transitions/transition-interpolator.ts
1239
+ var import_core4 = require("@math.gl/core");
1240
+
1241
+ // ../core/src/utils/assert.ts
1242
+ function assert(condition, message) {
1243
+ if (!condition) {
1244
+ throw new Error(message || "deck.gl: assertion failed.");
1245
+ }
1246
+ }
1247
+
1248
+ // ../core/src/transitions/transition-interpolator.ts
1249
+ var TransitionInterpolator = class {
1250
+ /**
1251
+ * @param opts {array|object}
1252
+ * @param opts.compare {array} - prop names used in equality check
1253
+ * @param opts.extract {array} - prop names needed for interpolation
1254
+ * @param opts.required {array} - prop names that must be supplied
1255
+ * alternatively, supply one list of prop names as `opts` if all of the above are the same.
1256
+ */
1257
+ constructor(opts) {
1258
+ const { compare, extract, required } = opts;
1259
+ this._propsToCompare = compare;
1260
+ this._propsToExtract = extract || compare;
1261
+ this._requiredProps = required;
1262
+ }
1263
+ /**
1264
+ * Checks if two sets of props need transition in between
1265
+ * @param currentProps {object} - a list of viewport props
1266
+ * @param nextProps {object} - a list of viewport props
1267
+ * @returns {bool} - true if two props are equivalent
1268
+ */
1269
+ arePropsEqual(currentProps, nextProps) {
1270
+ for (const key of this._propsToCompare) {
1271
+ if (!(key in currentProps) || !(key in nextProps) || !(0, import_core4.equals)(currentProps[key], nextProps[key])) {
1272
+ return false;
1273
+ }
1274
+ }
1275
+ return true;
1276
+ }
1277
+ /**
1278
+ * Called before transition starts to validate/pre-process start and end props
1279
+ * @param startProps {object} - a list of starting viewport props
1280
+ * @param endProps {object} - a list of target viewport props
1281
+ * @returns {Object} {start, end} - start and end props to be passed
1282
+ * to `interpolateProps`
1283
+ */
1284
+ initializeProps(startProps, endProps) {
1285
+ const startViewStateProps = {};
1286
+ const endViewStateProps = {};
1287
+ for (const key of this._propsToExtract) {
1288
+ if (key in startProps || key in endProps) {
1289
+ startViewStateProps[key] = startProps[key];
1290
+ endViewStateProps[key] = endProps[key];
1291
+ }
1292
+ }
1293
+ this._checkRequiredProps(startViewStateProps);
1294
+ this._checkRequiredProps(endViewStateProps);
1295
+ return { start: startViewStateProps, end: endViewStateProps };
1296
+ }
1297
+ /**
1298
+ * Returns transition duration
1299
+ * @param startProps {object} - a list of starting viewport props
1300
+ * @param endProps {object} - a list of target viewport props
1301
+ * @returns {Number} - transition duration in milliseconds
1302
+ */
1303
+ getDuration(startProps, endProps) {
1304
+ return endProps.transitionDuration;
1305
+ }
1306
+ _checkRequiredProps(props) {
1307
+ if (!this._requiredProps) {
1308
+ return;
1309
+ }
1310
+ this._requiredProps.forEach((propName) => {
1311
+ const value = props[propName];
1312
+ assert(
1313
+ Number.isFinite(value) || Array.isArray(value),
1314
+ `${propName} is required for transition`
1315
+ );
1316
+ });
1317
+ }
1318
+ };
1319
+
1320
+ // ../core/src/transitions/linear-interpolator.ts
1321
+ var import_core5 = require("@math.gl/core");
1322
+ var DEFAULT_PROPS = ["longitude", "latitude", "zoom", "bearing", "pitch"];
1323
+ var DEFAULT_REQUIRED_PROPS = ["longitude", "latitude", "zoom"];
1324
+ var LinearInterpolator = class extends TransitionInterpolator {
1325
+ /**
1326
+ * @param {Object} opts
1327
+ * @param {Array} opts.transitionProps - list of props to apply linear transition to.
1328
+ * @param {Array} opts.around - a screen point to zoom/rotate around.
1329
+ * @param {Function} opts.makeViewport - construct a viewport instance with given props.
1330
+ */
1331
+ constructor(opts = {}) {
1332
+ const transitionProps = Array.isArray(opts) ? opts : opts.transitionProps;
1333
+ const normalizedOpts = Array.isArray(opts) ? {} : opts;
1334
+ normalizedOpts.transitionProps = Array.isArray(transitionProps) ? {
1335
+ compare: transitionProps,
1336
+ required: transitionProps
1337
+ } : transitionProps || {
1338
+ compare: DEFAULT_PROPS,
1339
+ required: DEFAULT_REQUIRED_PROPS
1340
+ };
1341
+ super(normalizedOpts.transitionProps);
1342
+ this.opts = normalizedOpts;
1343
+ }
1344
+ initializeProps(startProps, endProps) {
1345
+ const result = super.initializeProps(startProps, endProps);
1346
+ const { makeViewport, around } = this.opts;
1347
+ if (makeViewport && around) {
1348
+ const startViewport = makeViewport(startProps);
1349
+ const endViewport = makeViewport(endProps);
1350
+ const aroundPosition = startViewport.unproject(around);
1351
+ result.start.around = around;
1352
+ Object.assign(result.end, {
1353
+ around: endViewport.project(aroundPosition),
1354
+ aroundPosition,
1355
+ width: endProps.width,
1356
+ height: endProps.height
1357
+ });
1358
+ }
1359
+ return result;
1360
+ }
1361
+ interpolateProps(startProps, endProps, t) {
1362
+ const propsInTransition = {};
1363
+ for (const key of this._propsToExtract) {
1364
+ propsInTransition[key] = (0, import_core5.lerp)(startProps[key] || 0, endProps[key] || 0, t);
1365
+ }
1366
+ if (endProps.aroundPosition && this.opts.makeViewport) {
1367
+ const viewport = this.opts.makeViewport({ ...endProps, ...propsInTransition });
1368
+ Object.assign(
1369
+ propsInTransition,
1370
+ viewport.panByPosition(
1371
+ endProps.aroundPosition,
1372
+ // anchor point in current screen coordinates
1373
+ (0, import_core5.lerp)(startProps.around, endProps.around, t)
1374
+ )
1375
+ );
1376
+ }
1377
+ return propsInTransition;
1378
+ }
1379
+ };
1380
+
1381
+ // ../core/src/controllers/controller.ts
1382
+ var import_web_mercator3 = require("@math.gl/web-mercator");
1383
+ var NO_TRANSITION_PROPS = {
1384
+ transitionDuration: 0
1385
+ };
1386
+ var DEFAULT_INERTIA = 300;
1387
+ var REBOUND_DURATION = 300;
1388
+ var INERTIA_EASING = (t) => 1 - (1 - t) * (1 - t);
1389
+ var EASE_OUT_EXPONENTIAL = (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
1390
+ var EVENT_TYPES = {
1391
+ WHEEL: ["wheel"],
1392
+ PAN: ["panstart", "panmove", "panend"],
1393
+ PINCH: ["pinchstart", "pinchmove", "pinchend"],
1394
+ MULTI_PAN: ["multipanstart", "multipanmove", "multipanend"],
1395
+ DOUBLE_CLICK: ["dblclick"],
1396
+ DOUBLE_CLICK_DRAG: [
1397
+ "dblclickdragstart",
1398
+ "dblclickdragmove",
1399
+ "dblclickdragend",
1400
+ "dblclickdragcancel"
1401
+ ],
1402
+ KEYBOARD: ["keydown"]
1403
+ };
1404
+ var pinchEventWorkaround = {};
1405
+ var Controller = class {
1406
+ constructor(opts) {
1407
+ this.state = {};
1408
+ this._events = {};
1409
+ this._interactionState = {
1410
+ isDragging: false
1411
+ };
1412
+ this._customEvents = [];
1413
+ this._eventStartBlocked = null;
1414
+ this._panMove = false;
1415
+ this._multiPanMode = null;
1416
+ this._multiPanStartCenter = null;
1417
+ this._doubleClickDragAnchor = null;
1418
+ this._suppressDoubleClickUntil = 0;
1419
+ this.invertPan = false;
1420
+ this.dragMode = "rotate";
1421
+ this.inertia = 0;
1422
+ this.scrollZoom = true;
1423
+ this.dragPan = true;
1424
+ this.dragRotate = true;
1425
+ this.doubleClickZoom = true;
1426
+ this.doubleClickDragZoom = true;
1427
+ this.touchZoom = true;
1428
+ this.touchRotate = false;
1429
+ this.multiTouchDrag = null;
1430
+ this.trackpadGesture = false;
1431
+ this.zoomAround = "pointer";
1432
+ this.keyboard = true;
1433
+ this.transitionManager = new TransitionManager({
1434
+ ...opts,
1435
+ getControllerState: (props, constraintContext) => new this.ControllerState({
1436
+ ...props,
1437
+ constraintContext,
1438
+ makeViewport: opts.makeViewport
1439
+ }),
1440
+ onViewStateChange: this._onTransition.bind(this),
1441
+ onStateChange: this._setInteractionState.bind(this)
1442
+ });
1443
+ this.handleEvent = this.handleEvent.bind(this);
1444
+ this.eventManager = opts.eventManager;
1445
+ this.onViewStateChange = opts.onViewStateChange || (() => {
1446
+ });
1447
+ this.onStateChange = opts.onStateChange || (() => {
1448
+ });
1449
+ this.makeViewport = opts.makeViewport;
1450
+ this.pickPosition = opts.pickPosition;
1451
+ }
1452
+ set events(customEvents) {
1453
+ this.toggleEvents(this._customEvents, false);
1454
+ this.toggleEvents(customEvents, true);
1455
+ this._customEvents = customEvents;
1456
+ if (this.props) {
1457
+ this.setProps(this.props);
1458
+ }
1459
+ }
1460
+ finalize() {
1461
+ var _a;
1462
+ for (const eventName in this._events) {
1463
+ if (this._events[eventName]) {
1464
+ (_a = this.eventManager) == null ? void 0 : _a.off(eventName, this.handleEvent);
1465
+ }
1466
+ }
1467
+ this.transitionManager.finalize();
1468
+ }
1469
+ /**
1470
+ * Callback for events
1471
+ */
1472
+ handleEvent(event) {
1473
+ this._controllerState = void 0;
1474
+ const eventStartBlocked = this._eventStartBlocked;
1475
+ switch (event.type) {
1476
+ case "panstart":
1477
+ return eventStartBlocked ? false : this._onPanStart(event);
1478
+ case "panmove":
1479
+ return this._onPan(event);
1480
+ case "panend":
1481
+ return this._onPanEnd(event);
1482
+ case "pinchstart":
1483
+ return eventStartBlocked || !this._isTrackpadGestureAllowed(event) ? false : this._onPinchStart(event);
1484
+ case "pinchmove":
1485
+ return this._isTrackpadGestureAllowed(event) ? this._onPinch(event) : false;
1486
+ case "pinchend":
1487
+ return this._isTrackpadGestureAllowed(event) ? this._onPinchEnd(event) : false;
1488
+ case "multipanstart":
1489
+ return eventStartBlocked ? false : this._onMultiPanStart(event);
1490
+ case "multipanmove":
1491
+ return this._onMultiPan(event);
1492
+ case "multipanend":
1493
+ return this._onMultiPanEnd(event);
1494
+ case "dblclick":
1495
+ return this._onDoubleClick(event);
1496
+ case "dblclickdragstart":
1497
+ return eventStartBlocked ? false : this._onDoubleClickDragStart(event);
1498
+ case "dblclickdragmove":
1499
+ return this._onDoubleClickDrag(event);
1500
+ case "dblclickdragend":
1501
+ case "dblclickdragcancel":
1502
+ return this._onDoubleClickDragEnd(event);
1503
+ case "wheel":
1504
+ return this._onWheel(event);
1505
+ case "keydown":
1506
+ return this._onKeyDown(event);
1507
+ default:
1508
+ return false;
1509
+ }
1510
+ }
1511
+ /* Event utils */
1512
+ // Event object: http://hammerjs.github.io/api/#event-object
1513
+ get controllerState() {
1514
+ this._controllerState = this._controllerState || new this.ControllerState({
1515
+ makeViewport: this.makeViewport,
1516
+ ...this.props,
1517
+ ...this.state
1518
+ });
1519
+ return this._controllerState;
1520
+ }
1521
+ getCenter(event) {
1522
+ const { x, y } = this.props;
1523
+ const { offsetCenter } = event;
1524
+ return [offsetCenter.x - x, offsetCenter.y - y];
1525
+ }
1526
+ /** Resolves the zoom anchor for pointer-based controls. */
1527
+ getZoomPosition(position) {
1528
+ if (this.zoomAround === "pointer") {
1529
+ return position;
1530
+ }
1531
+ const viewport = this.makeViewport(this.controllerState.getViewportProps());
1532
+ const [centerX, centerY] = (0, import_web_mercator3.worldToPixels)(viewport.center, viewport.pixelProjectionMatrix);
1533
+ return [centerX, centerY];
1534
+ }
1535
+ isPointInBounds(pos, event) {
1536
+ const { width, height } = this.props;
1537
+ if (event && event.handled) {
1538
+ return false;
1539
+ }
1540
+ const inside = pos[0] >= 0 && pos[0] <= width && pos[1] >= 0 && pos[1] <= height;
1541
+ if (inside && event) {
1542
+ event.stopPropagation();
1543
+ }
1544
+ return inside;
1545
+ }
1546
+ isFunctionKeyPressed(event) {
1547
+ const { srcEvent } = event;
1548
+ return Boolean(srcEvent.metaKey || srcEvent.altKey || srcEvent.ctrlKey || srcEvent.shiftKey);
1549
+ }
1550
+ isDragging() {
1551
+ return this._interactionState.isDragging || false;
1552
+ }
1553
+ // When a multi-touch event ends, e.g. pinch, not all pointers are lifted at the same time.
1554
+ // This triggers a brief `pan` event.
1555
+ // Calling this method will temporarily disable *start events to avoid conflicting transitions.
1556
+ blockEvents(timeout) {
1557
+ const timer = setTimeout(() => {
1558
+ if (this._eventStartBlocked === timer) {
1559
+ this._eventStartBlocked = null;
1560
+ }
1561
+ }, timeout);
1562
+ this._eventStartBlocked = timer;
1563
+ }
1564
+ /**
1565
+ * Extract interactivity options
1566
+ */
1567
+ setProps(props) {
1568
+ if (props.maxBoundsPadding === void 0) {
1569
+ props.maxBoundsPadding = null;
1570
+ }
1571
+ if (props.dragMode) {
1572
+ this.dragMode = props.dragMode;
1573
+ }
1574
+ const oldProps = this.props;
1575
+ this.props = props;
1576
+ if (!("transitionInterpolator" in props)) {
1577
+ props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
1578
+ }
1579
+ this.transitionManager.processViewStateChange(props);
1580
+ const { inertia } = props;
1581
+ this.inertia = Number.isFinite(inertia) ? inertia : inertia === true ? DEFAULT_INERTIA : 0;
1582
+ const {
1583
+ scrollZoom = true,
1584
+ dragPan = true,
1585
+ dragRotate = true,
1586
+ doubleClickZoom = true,
1587
+ doubleClickDragZoom = false,
1588
+ touchZoom = true,
1589
+ touchRotate = false,
1590
+ multiTouchDrag = touchRotate ? "rotate" : null,
1591
+ trackpadGesture = false,
1592
+ zoomAround = "pointer",
1593
+ keyboard = true
1594
+ } = props;
1595
+ const isInteractive = Boolean(this.onViewStateChange);
1596
+ this.toggleEvents(EVENT_TYPES.WHEEL, isInteractive && scrollZoom);
1597
+ this.toggleEvents(EVENT_TYPES.PAN, isInteractive);
1598
+ this.toggleEvents(
1599
+ EVENT_TYPES.PINCH,
1600
+ isInteractive && (touchZoom || multiTouchDrag === "rotate")
1601
+ );
1602
+ this.toggleEvents(EVENT_TYPES.MULTI_PAN, isInteractive && Boolean(multiTouchDrag));
1603
+ this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK, isInteractive && doubleClickZoom);
1604
+ this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK_DRAG, isInteractive && doubleClickDragZoom);
1605
+ this.toggleEvents(EVENT_TYPES.KEYBOARD, isInteractive && keyboard);
1606
+ this.scrollZoom = scrollZoom;
1607
+ this.dragPan = dragPan;
1608
+ this.dragRotate = dragRotate;
1609
+ this.doubleClickZoom = doubleClickZoom;
1610
+ this.doubleClickDragZoom = doubleClickDragZoom;
1611
+ this.touchZoom = touchZoom;
1612
+ this.touchRotate = multiTouchDrag === "rotate";
1613
+ this.multiTouchDrag = multiTouchDrag;
1614
+ this.trackpadGesture = trackpadGesture;
1615
+ this.zoomAround = zoomAround;
1616
+ this.keyboard = keyboard;
1617
+ const constraintChanged = !oldProps || oldProps.height !== props.height || oldProps.width !== props.width || oldProps.maxBounds !== props.maxBounds || oldProps.maxBoundsPadding !== props.maxBoundsPadding;
1618
+ if (constraintChanged && props.maxBounds) {
1619
+ const controllerState = new this.ControllerState({ ...props, makeViewport: this.makeViewport });
1620
+ const normalizedProps = controllerState.getViewportProps();
1621
+ const changed = Object.keys(normalizedProps).some(
1622
+ (key) => !deepEqual(normalizedProps[key], props[key], 1)
1623
+ );
1624
+ if (changed) {
1625
+ this.updateViewport(controllerState);
1626
+ }
1627
+ }
1628
+ }
1629
+ updateTransition() {
1630
+ this.transitionManager.updateTransition();
1631
+ }
1632
+ toggleEvents(eventNames, enabled) {
1633
+ if (this.eventManager) {
1634
+ eventNames.forEach((eventName) => {
1635
+ if (this._events[eventName] !== enabled) {
1636
+ this._events[eventName] = enabled;
1637
+ if (enabled) {
1638
+ this.eventManager.on(eventName, this.handleEvent);
1639
+ } else {
1640
+ this.eventManager.off(eventName, this.handleEvent);
1641
+ }
1642
+ }
1643
+ });
1644
+ }
1645
+ }
1646
+ // Private Methods
1647
+ /* Callback util */
1648
+ // formats map state and invokes callback function
1649
+ updateViewport(newControllerState, extraProps = null, interactionState = {}) {
1650
+ const viewState = { ...newControllerState.getViewportProps(), ...extraProps };
1651
+ const changed = this.controllerState !== newControllerState;
1652
+ this.state = newControllerState.getState();
1653
+ this._setInteractionState(interactionState);
1654
+ if (changed) {
1655
+ const oldViewState = this.controllerState && this.controllerState.getViewportProps();
1656
+ if (this.onViewStateChange) {
1657
+ this.onViewStateChange({
1658
+ viewState,
1659
+ interactionState: this._interactionState,
1660
+ oldViewState,
1661
+ viewId: this.props.id
1662
+ });
1663
+ }
1664
+ }
1665
+ }
1666
+ _onTransition(params) {
1667
+ this.onViewStateChange({
1668
+ ...params,
1669
+ interactionState: this._interactionState,
1670
+ viewId: this.props.id
1671
+ });
1672
+ }
1673
+ _setInteractionState(newStates) {
1674
+ Object.assign(this._interactionState, newStates);
1675
+ this.onStateChange(this._interactionState);
1676
+ }
1677
+ /** Maps a semantic input lifecycle to the constraint policy seen by controller state. */
1678
+ _getConstraintContext(_action, phase) {
1679
+ if (!this.props.rubberBand) {
1680
+ return { mode: "hard" };
1681
+ }
1682
+ return { mode: phase === "update" ? "elastic" : phase === "end" ? "rebound" : "hard" };
1683
+ }
1684
+ /** Returns a rebound transition when hard resolution changed the displayed viewport props. */
1685
+ _getReboundTransition(constraintContext, nextControllerState) {
1686
+ if (constraintContext.mode !== "rebound") {
1687
+ return null;
1688
+ }
1689
+ const nextViewportProps = nextControllerState.getViewportProps();
1690
+ const shouldRebound = Object.keys(nextViewportProps).some(
1691
+ (key) => !deepEqual(this.props[key], nextViewportProps[key], 1)
1692
+ );
1693
+ return shouldRebound ? {
1694
+ ...this._getTransitionProps(),
1695
+ transitionDuration: REBOUND_DURATION,
1696
+ transitionEasing: EASE_OUT_EXPONENTIAL
1697
+ } : null;
1698
+ }
1699
+ /* Event handlers */
1700
+ // Default handler for the `panstart` event.
1701
+ _onPanStart(event) {
1702
+ const pos = this.getCenter(event);
1703
+ if (!this.isPointInBounds(pos, event)) {
1704
+ return false;
1705
+ }
1706
+ let alternateMode = this.isFunctionKeyPressed(event) || event.rightButton || false;
1707
+ if (this.invertPan || this.dragMode === "pan") {
1708
+ alternateMode = !alternateMode;
1709
+ }
1710
+ const action = alternateMode ? "pan" : "rotate";
1711
+ const constraintContext = this._getConstraintContext(action, "start");
1712
+ const newControllerState = alternateMode ? this.controllerState.panStart({ pos }, constraintContext) : this.controllerState.rotateStart({ pos }, constraintContext);
1713
+ this._panMove = alternateMode;
1714
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
1715
+ return true;
1716
+ }
1717
+ // Default handler for the `panmove` and `panend` event.
1718
+ _onPan(event) {
1719
+ if (!this.isDragging()) {
1720
+ return false;
1721
+ }
1722
+ return this._panMove ? this._onPanMove(event) : this._onPanRotate(event);
1723
+ }
1724
+ _onPanEnd(event) {
1725
+ if (!this.isDragging()) {
1726
+ return false;
1727
+ }
1728
+ return this._panMove ? this._onPanMoveEnd(event) : this._onPanRotateEnd(event);
1729
+ }
1730
+ // Default handler for panning to move.
1731
+ // Called by `_onPan` when panning without function key pressed.
1732
+ _onPanMove(event) {
1733
+ if (!this.dragPan) {
1734
+ return false;
1735
+ }
1736
+ const pos = this.getCenter(event);
1737
+ const newControllerState = this.controllerState.pan(
1738
+ { pos },
1739
+ this._getConstraintContext("pan", "update")
1740
+ );
1741
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
1742
+ isDragging: true,
1743
+ isPanning: true
1744
+ });
1745
+ return true;
1746
+ }
1747
+ _onPanMoveEnd(event) {
1748
+ const { inertia } = this;
1749
+ if (this.dragPan && inertia && event.velocity) {
1750
+ const pos = this.getCenter(event);
1751
+ const endPos = [
1752
+ pos[0] + event.velocityX * inertia / 2,
1753
+ pos[1] + event.velocityY * inertia / 2
1754
+ ];
1755
+ const newControllerState = this.controllerState.pan({ pos: endPos }).panEnd();
1756
+ this.updateViewport(
1757
+ newControllerState,
1758
+ {
1759
+ ...this._getTransitionProps(),
1760
+ transitionDuration: inertia,
1761
+ transitionEasing: INERTIA_EASING
1762
+ },
1763
+ {
1764
+ isDragging: false,
1765
+ isPanning: true
1766
+ }
1767
+ );
1768
+ } else {
1769
+ const currentControllerState = this.controllerState;
1770
+ const constraintContext = this._getConstraintContext("pan", "end");
1771
+ const newControllerState = currentControllerState.panEnd(constraintContext);
1772
+ const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
1773
+ this.updateViewport(newControllerState, reboundTransition, {
1774
+ isDragging: false,
1775
+ isPanning: Boolean(reboundTransition)
1776
+ });
1777
+ }
1778
+ return true;
1779
+ }
1780
+ // Default handler for panning to rotate.
1781
+ // Called by `_onPan` when panning with function key pressed.
1782
+ _onPanRotate(event) {
1783
+ if (!this.dragRotate) {
1784
+ return false;
1785
+ }
1786
+ const pos = this.getCenter(event);
1787
+ const newControllerState = this.controllerState.rotate(
1788
+ { pos },
1789
+ this._getConstraintContext("rotate", "update")
1790
+ );
1791
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
1792
+ isDragging: true,
1793
+ isRotating: true
1794
+ });
1795
+ return true;
1796
+ }
1797
+ _onPanRotateEnd(event) {
1798
+ const { inertia } = this;
1799
+ if (this.dragRotate && inertia && event.velocity) {
1800
+ const pos = this.getCenter(event);
1801
+ const endPos = [
1802
+ pos[0] + event.velocityX * inertia / 2,
1803
+ pos[1] + event.velocityY * inertia / 2
1804
+ ];
1805
+ const newControllerState = this.controllerState.rotate({ pos: endPos }).rotateEnd();
1806
+ this.updateViewport(
1807
+ newControllerState,
1808
+ {
1809
+ ...this._getTransitionProps(),
1810
+ transitionDuration: inertia,
1811
+ transitionEasing: INERTIA_EASING
1812
+ },
1813
+ {
1814
+ isDragging: false,
1815
+ isRotating: true
1816
+ }
1817
+ );
1818
+ } else {
1819
+ const currentControllerState = this.controllerState;
1820
+ const constraintContext = this._getConstraintContext("rotate", "end");
1821
+ const newControllerState = currentControllerState.rotateEnd(constraintContext);
1822
+ const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
1823
+ this.updateViewport(newControllerState, reboundTransition, {
1824
+ isDragging: false,
1825
+ isRotating: Boolean(reboundTransition)
1826
+ });
1827
+ }
1828
+ return true;
1829
+ }
1830
+ // Default handler for the `wheel` event.
1831
+ _onWheel(event) {
1832
+ if (!this.scrollZoom) {
1833
+ return false;
1834
+ }
1835
+ if (this.trackpadGesture && event.device !== "mouse") {
1836
+ return false;
1837
+ }
1838
+ const pos = this.getCenter(event);
1839
+ if (!this.isPointInBounds(pos, event)) {
1840
+ return false;
1841
+ }
1842
+ event.srcEvent.preventDefault();
1843
+ const { speed = 0.01, smooth = false } = this.scrollZoom === true ? {} : this.scrollZoom;
1844
+ const { delta } = event;
1845
+ let scale = 2 / (1 + Math.exp(-Math.abs(delta * speed)));
1846
+ if (delta < 0 && scale !== 0) {
1847
+ scale = 1 / scale;
1848
+ }
1849
+ const zoomPosition = this.getZoomPosition(pos);
1850
+ const transitionProps = smooth ? { ...this._getTransitionProps({ around: zoomPosition }), transitionDuration: 250 } : NO_TRANSITION_PROPS;
1851
+ const newControllerState = this.controllerState.zoom({ pos: zoomPosition, scale });
1852
+ this.updateViewport(newControllerState, transitionProps, {
1853
+ isZooming: true,
1854
+ isPanning: true
1855
+ });
1856
+ if (!smooth) {
1857
+ this._setInteractionState({ isZooming: false, isPanning: false });
1858
+ }
1859
+ return true;
1860
+ }
1861
+ _onMultiPanStart(event) {
1862
+ const { multiTouchDrag } = this;
1863
+ if (!multiTouchDrag || !this._isMultiPanEventAllowed(event, multiTouchDrag)) {
1864
+ return false;
1865
+ }
1866
+ const currentCenter = event.offsetCenter;
1867
+ if (!this.isPointInBounds(this.getCenter(event), event)) {
1868
+ return false;
1869
+ }
1870
+ const isTrackpad = event.pointerType === "trackpad";
1871
+ const startCenter = {
1872
+ x: currentCenter.x - (isTrackpad ? 0 : event.deltaX),
1873
+ y: currentCenter.y - (isTrackpad ? 0 : event.deltaY)
1874
+ };
1875
+ const startEvent = { ...event, offsetCenter: startCenter };
1876
+ const pos = this.getCenter(startEvent);
1877
+ const newControllerState = multiTouchDrag === "pan" ? this.controllerState.panStart({ pos }, this._getConstraintContext("pan", "start")) : this.controllerState.rotateStart({ pos }, this._getConstraintContext("rotate", "start"));
1878
+ this._multiPanMode = multiTouchDrag;
1879
+ this._multiPanStartCenter = startCenter;
1880
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
1881
+ return true;
1882
+ }
1883
+ _onMultiPan(event) {
1884
+ const { mode, event: panEvent } = this._getMultiPanEvent(event);
1885
+ if (!mode || !panEvent || !this.isDragging()) {
1886
+ return false;
1887
+ }
1888
+ return mode === "pan" ? this._onPanMove(panEvent) : this._onPanRotate(panEvent);
1889
+ }
1890
+ _onMultiPanEnd(event) {
1891
+ const { mode, event: panEvent } = this._getMultiPanEvent(event);
1892
+ if (!mode || !panEvent || !this.isDragging()) {
1893
+ this._resetMultiPan();
1894
+ return false;
1895
+ }
1896
+ const handled = mode === "pan" ? this._onPanMoveEnd(panEvent) : this._onPanRotateEnd(panEvent);
1897
+ this._resetMultiPan();
1898
+ return handled;
1899
+ }
1900
+ _isTrackpadGestureAllowed(event) {
1901
+ return event.pointerType !== "trackpad" || this.trackpadGesture;
1902
+ }
1903
+ _isMultiPanEventAllowed(event, mode) {
1904
+ if (event.pointerType === "trackpad") {
1905
+ return this.trackpadGesture && (mode === "pan" ? this.dragPan : this.dragRotate);
1906
+ }
1907
+ return event.pointerType === "touch" && (mode === "pan" ? this.dragPan : this.dragRotate);
1908
+ }
1909
+ _getMultiPanEvent(event) {
1910
+ const mode = this._multiPanMode;
1911
+ const startCenter = this._multiPanStartCenter;
1912
+ if (!mode || !startCenter) {
1913
+ return { mode: null, event: null };
1914
+ }
1915
+ return {
1916
+ mode,
1917
+ event: {
1918
+ ...event,
1919
+ offsetCenter: {
1920
+ x: startCenter.x + event.deltaX,
1921
+ y: startCenter.y + event.deltaY
1922
+ }
1923
+ }
1924
+ };
1925
+ }
1926
+ _resetMultiPan() {
1927
+ this._multiPanMode = null;
1928
+ this._multiPanStartCenter = null;
1929
+ }
1930
+ // Default handler for the `pinchstart` event.
1931
+ _onPinchStart(event) {
1932
+ this._doubleClickDragAnchor = null;
1933
+ const pos = this.getCenter(event);
1934
+ if (!this.isPointInBounds(pos, event)) {
1935
+ return false;
1936
+ }
1937
+ const newControllerState = this.controllerState.zoomStart({ pos: this.getZoomPosition(pos) }, this._getConstraintContext("zoom", "start")).rotateStart({ pos }, this._getConstraintContext("rotate", "start"));
1938
+ pinchEventWorkaround._startPinchRotation = event.rotation;
1939
+ pinchEventWorkaround._lastPinchEvent = event;
1940
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
1941
+ return true;
1942
+ }
1943
+ // Default handler for the `pinchmove` and `pinchend` events.
1944
+ _onPinch(event) {
1945
+ if (!this.touchZoom && !this.touchRotate) {
1946
+ return false;
1947
+ }
1948
+ if (!this.isDragging()) {
1949
+ return false;
1950
+ }
1951
+ let newControllerState = this.controllerState;
1952
+ if (this.touchZoom) {
1953
+ const { scale } = event;
1954
+ const pos = this.getCenter(event);
1955
+ newControllerState = newControllerState.zoom(
1956
+ { pos: this.getZoomPosition(pos), scale },
1957
+ this._getConstraintContext("zoom", "update")
1958
+ );
1959
+ }
1960
+ if (this.touchRotate) {
1961
+ const { rotation } = event;
1962
+ newControllerState = newControllerState.rotate(
1963
+ { deltaAngleX: pinchEventWorkaround._startPinchRotation - rotation },
1964
+ this._getConstraintContext("rotate", "update")
1965
+ );
1966
+ }
1967
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
1968
+ isDragging: true,
1969
+ isPanning: this.touchZoom,
1970
+ isZooming: this.touchZoom,
1971
+ isRotating: this.touchRotate
1972
+ });
1973
+ pinchEventWorkaround._lastPinchEvent = event;
1974
+ return true;
1975
+ }
1976
+ _onPinchEnd(event) {
1977
+ if (!this.isDragging()) {
1978
+ return false;
1979
+ }
1980
+ const { inertia } = this;
1981
+ const { _lastPinchEvent } = pinchEventWorkaround;
1982
+ if (this.touchZoom && inertia && _lastPinchEvent && event.scale !== _lastPinchEvent.scale) {
1983
+ const pos = this.getCenter(event);
1984
+ const zoomPosition = this.getZoomPosition(pos);
1985
+ let newControllerState = this.controllerState.rotateEnd();
1986
+ const z = Math.log2(event.scale);
1987
+ const velocityZ = (z - Math.log2(_lastPinchEvent.scale)) / (event.deltaTime - _lastPinchEvent.deltaTime);
1988
+ const endScale = Math.pow(2, z + velocityZ * inertia / 2);
1989
+ newControllerState = newControllerState.zoom({ pos: zoomPosition, scale: endScale }).zoomEnd();
1990
+ this.updateViewport(
1991
+ newControllerState,
1992
+ {
1993
+ ...this._getTransitionProps({ around: zoomPosition }),
1994
+ transitionDuration: inertia,
1995
+ transitionEasing: INERTIA_EASING
1996
+ },
1997
+ {
1998
+ isDragging: false,
1999
+ isPanning: this.touchZoom,
2000
+ isZooming: this.touchZoom,
2001
+ isRotating: false
2002
+ }
2003
+ );
2004
+ this.blockEvents(inertia);
2005
+ } else {
2006
+ const currentControllerState = this.controllerState;
2007
+ const zoomConstraintContext = this._getConstraintContext("zoom", "end");
2008
+ const rotateConstraintContext = this._getConstraintContext("rotate", "end");
2009
+ const newControllerState = currentControllerState.zoomEnd(zoomConstraintContext).rotateEnd(rotateConstraintContext);
2010
+ const reboundTransition = this._getReboundTransition(
2011
+ this.touchZoom ? zoomConstraintContext : rotateConstraintContext,
2012
+ newControllerState
2013
+ );
2014
+ this.updateViewport(newControllerState, reboundTransition, {
2015
+ isDragging: false,
2016
+ isPanning: Boolean(reboundTransition) && this.touchZoom,
2017
+ isZooming: Boolean(reboundTransition) && this.touchZoom,
2018
+ isRotating: Boolean(reboundTransition) && this.touchRotate
2019
+ });
2020
+ }
2021
+ pinchEventWorkaround._startPinchRotation = null;
2022
+ pinchEventWorkaround._lastPinchEvent = null;
2023
+ return true;
2024
+ }
2025
+ // Default handler for the `dblclick` event.
2026
+ _onDoubleClick(event) {
2027
+ if (!this.doubleClickZoom) {
2028
+ return false;
2029
+ }
2030
+ if (Date.now() < this._suppressDoubleClickUntil) {
2031
+ return false;
2032
+ }
2033
+ const pos = this.getCenter(event);
2034
+ if (!this.isPointInBounds(pos, event)) {
2035
+ return false;
2036
+ }
2037
+ const isZoomOut = this.isFunctionKeyPressed(event);
2038
+ const zoomPosition = this.getZoomPosition(pos);
2039
+ const newControllerState = this.controllerState.zoom({
2040
+ pos: zoomPosition,
2041
+ scale: isZoomOut ? 0.5 : 2
2042
+ });
2043
+ this.updateViewport(newControllerState, this._getTransitionProps({ around: zoomPosition }), {
2044
+ isZooming: true,
2045
+ isPanning: true
2046
+ });
2047
+ this.blockEvents(100);
2048
+ return true;
2049
+ }
2050
+ _onDoubleClickDragStart(event) {
2051
+ if (!this.doubleClickDragZoom) {
2052
+ this._doubleClickDragAnchor = null;
2053
+ return false;
2054
+ }
2055
+ const pos = this.getCenter(event);
2056
+ if (!this.isPointInBounds(pos, event)) {
2057
+ this._doubleClickDragAnchor = null;
2058
+ return false;
2059
+ }
2060
+ this._doubleClickDragAnchor = this.getZoomPosition(pos);
2061
+ let newControllerState = this.controllerState.zoomStart(
2062
+ { pos: this._doubleClickDragAnchor },
2063
+ this._getConstraintContext("zoom", "start")
2064
+ );
2065
+ if (event.scale !== 1) {
2066
+ newControllerState = newControllerState.zoom(
2067
+ { pos: this._doubleClickDragAnchor, scale: event.scale },
2068
+ this._getConstraintContext("zoom", "update")
2069
+ );
2070
+ }
2071
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
2072
+ isDragging: true,
2073
+ isPanning: true,
2074
+ isZooming: true
2075
+ });
2076
+ return true;
2077
+ }
2078
+ _onDoubleClickDrag(event) {
2079
+ const pos = this._doubleClickDragAnchor;
2080
+ if (!pos) {
2081
+ return false;
2082
+ }
2083
+ const newControllerState = this.controllerState.zoom(
2084
+ { pos, scale: event.scale },
2085
+ this._getConstraintContext("zoom", "update")
2086
+ );
2087
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
2088
+ isDragging: true,
2089
+ isPanning: true,
2090
+ isZooming: true
2091
+ });
2092
+ return true;
2093
+ }
2094
+ _onDoubleClickDragEnd(_event) {
2095
+ const pos = this._doubleClickDragAnchor;
2096
+ if (!pos) {
2097
+ return false;
2098
+ }
2099
+ this._doubleClickDragAnchor = null;
2100
+ const currentControllerState = this.controllerState;
2101
+ const constraintContext = this._getConstraintContext("zoom", "end");
2102
+ const newControllerState = currentControllerState.zoomEnd(constraintContext);
2103
+ const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
2104
+ this.updateViewport(newControllerState, reboundTransition, {
2105
+ isDragging: false,
2106
+ isPanning: Boolean(reboundTransition),
2107
+ isZooming: Boolean(reboundTransition)
2108
+ });
2109
+ this._suppressDoubleClickUntil = Date.now() + 100;
2110
+ this.blockEvents(100);
2111
+ return true;
2112
+ }
2113
+ // Default handler for the `keydown` event
2114
+ _onKeyDown(event) {
2115
+ if (!this.keyboard) {
2116
+ return false;
2117
+ }
2118
+ const funcKey = this.isFunctionKeyPressed(event);
2119
+ const { zoomSpeed, moveSpeed, rotateSpeedX, rotateSpeedY } = this.keyboard === true ? {} : this.keyboard;
2120
+ const { controllerState } = this;
2121
+ let newControllerState;
2122
+ const interactionState = {};
2123
+ switch (event.srcEvent.code) {
2124
+ case "Minus":
2125
+ newControllerState = funcKey ? controllerState.zoomOut(zoomSpeed).zoomOut(zoomSpeed) : controllerState.zoomOut(zoomSpeed);
2126
+ interactionState.isZooming = true;
2127
+ break;
2128
+ case "Equal":
2129
+ newControllerState = funcKey ? controllerState.zoomIn(zoomSpeed).zoomIn(zoomSpeed) : controllerState.zoomIn(zoomSpeed);
2130
+ interactionState.isZooming = true;
2131
+ break;
2132
+ case "ArrowLeft":
2133
+ if (funcKey) {
2134
+ newControllerState = controllerState.rotateLeft(rotateSpeedX);
2135
+ interactionState.isRotating = true;
2136
+ } else {
2137
+ newControllerState = controllerState.moveLeft(moveSpeed);
2138
+ interactionState.isPanning = true;
2139
+ }
2140
+ break;
2141
+ case "ArrowRight":
2142
+ if (funcKey) {
2143
+ newControllerState = controllerState.rotateRight(rotateSpeedX);
2144
+ interactionState.isRotating = true;
2145
+ } else {
2146
+ newControllerState = controllerState.moveRight(moveSpeed);
2147
+ interactionState.isPanning = true;
2148
+ }
2149
+ break;
2150
+ case "ArrowUp":
2151
+ if (funcKey) {
2152
+ newControllerState = controllerState.rotateUp(rotateSpeedY);
2153
+ interactionState.isRotating = true;
2154
+ } else {
2155
+ newControllerState = controllerState.moveUp(moveSpeed);
2156
+ interactionState.isPanning = true;
2157
+ }
2158
+ break;
2159
+ case "ArrowDown":
2160
+ if (funcKey) {
2161
+ newControllerState = controllerState.rotateDown(rotateSpeedY);
2162
+ interactionState.isRotating = true;
2163
+ } else {
2164
+ newControllerState = controllerState.moveDown(moveSpeed);
2165
+ interactionState.isPanning = true;
2166
+ }
2167
+ break;
2168
+ default:
2169
+ return false;
2170
+ }
2171
+ this.updateViewport(newControllerState, this._getTransitionProps(), interactionState);
2172
+ return true;
2173
+ }
2174
+ _getTransitionProps(opts) {
2175
+ const { transition } = this;
2176
+ if (!transition || !transition.transitionInterpolator) {
2177
+ return NO_TRANSITION_PROPS;
2178
+ }
2179
+ return opts ? {
2180
+ ...transition,
2181
+ transitionInterpolator: new LinearInterpolator({
2182
+ ...opts,
2183
+ ...transition.transitionInterpolator.opts,
2184
+ makeViewport: this.controllerState.makeViewport
2185
+ })
2186
+ } : transition;
2187
+ }
2188
+ };
2189
+
2190
+ // ../core/src/controllers/view-state.ts
2191
+ var CONSTRAINT_AROUND = /* @__PURE__ */ Symbol("constraintAround");
2192
+ var ViewState = class {
2193
+ constructor(props, state, makeViewport, constraintContext) {
2194
+ this.makeViewport = makeViewport;
2195
+ this._viewportProps = this.applyConstraints(props, constraintContext);
2196
+ this._state = state;
2197
+ }
2198
+ getViewportProps() {
2199
+ return this._viewportProps;
2200
+ }
2201
+ getState() {
2202
+ return this._state;
2203
+ }
2204
+ };
2205
+
2206
+ // ../core/src/controllers/utils.ts
2207
+ function applyRubberBand(value, constrainedValue, range) {
2208
+ const overshoot = value - constrainedValue;
2209
+ return overshoot && Number.isFinite(overshoot) ? constrainedValue + overshoot * range / (range + Math.abs(overshoot)) : constrainedValue;
2210
+ }
2211
+ function getMaxBoundsRect(width, height, padding) {
2212
+ const left = getPosition(parsePosition((padding == null ? void 0 : padding.left) ?? 0), width);
2213
+ const right = getPosition(parsePosition((padding == null ? void 0 : padding.right) ?? 0), width);
2214
+ const top = getPosition(parsePosition((padding == null ? void 0 : padding.top) ?? 0), height);
2215
+ const bottom = getPosition(parsePosition((padding == null ? void 0 : padding.bottom) ?? 0), height);
2216
+ return {
2217
+ x: left,
2218
+ y: top,
2219
+ width: width - left - right,
2220
+ height: height - top - bottom
2221
+ };
2222
+ }
2223
+ function getMaxBoundsExtents(viewport, target, rect) {
2224
+ let [x, y] = viewport.project(target);
2225
+ x = Number.isFinite(x) ? x : viewport.width / 2;
2226
+ y = Number.isFinite(y) ? y : viewport.height / 2;
2227
+ return {
2228
+ left: x - rect.x,
2229
+ right: rect.x + rect.width - x,
2230
+ top: y - rect.y,
2231
+ bottom: rect.y + rect.height - y
2232
+ };
2233
+ }
2234
+
2235
+ // ../core/src/utils/apply-styles.ts
2236
+ function applyStyles(element, style) {
2237
+ if (style) {
2238
+ Object.entries(style).map(([key, value]) => {
2239
+ if (key.startsWith("--")) {
2240
+ element.style.setProperty(key, value);
2241
+ } else {
2242
+ element.style[key] = value;
2243
+ }
2244
+ });
2245
+ }
2246
+ }
2247
+ function removeStyles(element, style) {
2248
+ if (style) {
2249
+ Object.keys(style).map((key) => {
2250
+ if (key.startsWith("--")) {
2251
+ element.style.removeProperty(key);
2252
+ } else {
2253
+ element.style[key] = "";
2254
+ }
2255
+ });
2256
+ }
2257
+ }
2258
+
2259
+ // ../core/src/lib/widget.ts
2260
+ var Widget = class {
2261
+ constructor(props) {
2262
+ /**
2263
+ * The view id that this widget controls and is positioned relative to. Default `null`.
2264
+ * If assigned, this widget only responds to events inside the matching view. In multi-canvas
2265
+ * mode, the view's `canvasId` determines the positioning offset, but the widget DOM remains
2266
+ * under the shared widget root.
2267
+ */
2268
+ this.viewId = null;
2269
+ this.props = {
2270
+ // @ts-expect-error `defaultProps` may not exist on constructor
2271
+ ...this.constructor.defaultProps,
2272
+ ...props
2273
+ };
2274
+ this.id = this.props.id;
2275
+ }
2276
+ /** Called to update widget options */
2277
+ setProps(props) {
2278
+ const oldProps = this.props;
2279
+ const el = this.rootElement;
2280
+ if (el && oldProps.className !== props.className) {
2281
+ if (oldProps.className) el.classList.remove(oldProps.className);
2282
+ if (props.className) el.classList.add(props.className);
2283
+ }
2284
+ if (el && !deepEqual(oldProps.style, props.style, 1)) {
2285
+ removeStyles(el, oldProps.style);
2286
+ applyStyles(el, props.style);
2287
+ }
2288
+ Object.assign(this.props, props);
2289
+ this.updateHTML();
2290
+ }
2291
+ /** Update the HTML to reflect latest props and state */
2292
+ updateHTML() {
2293
+ if (this.rootElement) {
2294
+ this.onRenderHTML(this.rootElement);
2295
+ }
2296
+ }
2297
+ // VIEW STATE HELPERS
2298
+ get viewIds() {
2299
+ var _a;
2300
+ return this.viewId ? [this.viewId] : ((_a = this.deck) == null ? void 0 : _a.getViews().map((v) => v.id)) ?? [];
2301
+ }
2302
+ /** Returns the current view state for the given view */
2303
+ getViewState(viewId) {
2304
+ var _a, _b;
2305
+ return ((_b = (_a = this.deck) == null ? void 0 : _a.viewManager) == null ? void 0 : _b.getViewState(viewId)) || {};
2306
+ }
2307
+ /** Updates the view state for the given view */
2308
+ setViewState(viewId, viewState) {
2309
+ var _a;
2310
+ (_a = this.deck) == null ? void 0 : _a._onViewStateChange({ viewId, viewState, interactionState: {} });
2311
+ }
2312
+ // @note empty method calls have an overhead in V8 but it is very low, ~1ns
2313
+ /**
2314
+ * Common utility to create the root DOM element for this widget
2315
+ * Configures the top-level styles and adds basic class names for theming
2316
+ * @returns an UI element that should be appended to the Deck container
2317
+ */
2318
+ onCreateRootElement() {
2319
+ const CLASS_NAMES = [
2320
+ // Add class names for theming
2321
+ "deck-widget",
2322
+ this.className,
2323
+ // plus any app-supplied class name
2324
+ this.props.className
2325
+ ];
2326
+ const element = document.createElement("div");
2327
+ CLASS_NAMES.filter((cls) => typeof cls === "string" && cls.length > 0).forEach(
2328
+ (className) => element.classList.add(className)
2329
+ );
2330
+ applyStyles(element, this.props.style);
2331
+ return element;
2332
+ }
2333
+ /** Internal API called by Deck when the widget is first added to a Deck instance */
2334
+ _onAdd(params) {
2335
+ return this.onAdd(params) ?? this.onCreateRootElement();
2336
+ }
2337
+ /** Overridable by subclass - called when the widget is first added to a Deck instance
2338
+ * @returns an optional UI element that should be appended to the Deck container
2339
+ */
2340
+ onAdd(params) {
2341
+ }
2342
+ /** Called when the widget is removed */
2343
+ onRemove() {
2344
+ }
2345
+ // deck integration - Event hooks
2346
+ /** Called when the containing view is changed */
2347
+ onViewportChange(viewport) {
2348
+ }
2349
+ /** Called when the containing view is redrawn */
2350
+ onRedraw(params) {
2351
+ }
2352
+ /** Called when a hover event occurs */
2353
+ onHover(info, event) {
2354
+ }
2355
+ /** Called when a click event occurs */
2356
+ onClick(info, event) {
2357
+ }
2358
+ /** Called when a drag event occurs */
2359
+ onDrag(info, event) {
2360
+ }
2361
+ /** Called when a dragstart event occurs */
2362
+ onDragStart(info, event) {
2363
+ }
2364
+ /** Called when a dragend event occurs */
2365
+ onDragEnd(info, event) {
2366
+ }
2367
+ };
2368
+ Widget.defaultProps = {
2369
+ id: "widget",
2370
+ style: {},
2371
+ _container: null,
2372
+ className: ""
2373
+ };
2374
+
2375
+ // ../core/src/viewports/globe-viewport.ts
2376
+ var import_core6 = require("@math.gl/core");
2377
+ var import_web_mercator4 = require("@math.gl/web-mercator");
2378
+ var DEGREES_TO_RADIANS2 = Math.PI / 180;
2379
+ var RADIANS_TO_DEGREES = 180 / Math.PI;
2380
+ var NORTH_UP_BEARING_THRESHOLD = 1;
2381
+ var EARTH_RADIUS = 6370972;
2382
+ var GLOBE_RADIUS = 256;
2383
+ var GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO = 0.75;
2384
+ var GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO = 1.15;
2385
+ function isGlobeNorthUp(bearing) {
2386
+ const normalizedBearing = mod(bearing + 180, 360) - 180;
2387
+ return Math.abs(normalizedBearing) < NORTH_UP_BEARING_THRESHOLD;
2388
+ }
2389
+ function getDistanceScales2() {
2390
+ const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
2391
+ const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
2392
+ return {
2393
+ unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
2394
+ unitsPerMeter2: [0, 0, 0],
2395
+ metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
2396
+ unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
2397
+ unitsPerDegree2: [0, 0, 0],
2398
+ degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
2399
+ };
2400
+ }
2401
+ var GlobeViewport = class extends Viewport {
2402
+ constructor(opts = {}) {
2403
+ const {
2404
+ longitude = 0,
2405
+ bearing = 0,
2406
+ pitch = 0,
2407
+ zoom = 0,
2408
+ // Matches Maplibre defaults
2409
+ // https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
2410
+ nearZMultiplier = 0.5,
2411
+ farZMultiplier = 1,
2412
+ resolution = 10
2413
+ } = opts;
2414
+ let { latitude = 0, height, altitude = 1.5, fovy } = opts;
2415
+ latitude = Math.max(Math.min(latitude, 90), -90);
2416
+ height = height || 1;
2417
+ if (fovy) {
2418
+ altitude = (0, import_web_mercator4.fovyToAltitude)(fovy);
2419
+ } else {
2420
+ fovy = (0, import_web_mercator4.altitudeToFovy)(altitude);
2421
+ }
2422
+ const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator4.MAX_LATITUDE), -import_web_mercator4.MAX_LATITUDE);
2423
+ const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
2424
+ const pitchRadians = pitch * DEGREES_TO_RADIANS2;
2425
+ const nearZ = opts.nearZ ?? nearZMultiplier;
2426
+ const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
2427
+ const viewMatrix2 = new import_core6.Matrix4().lookAt({ eye: [0, -altitude, 0], up: [0, 0, 1] }).rotateX(-pitchRadians).rotateY(-bearing * DEGREES_TO_RADIANS2).rotateX(latitude * DEGREES_TO_RADIANS2).rotateZ(-longitude * DEGREES_TO_RADIANS2).scale(scale / height);
2428
+ super({
2429
+ ...opts,
2430
+ // x, y, width,
2431
+ height,
2432
+ // view matrix
2433
+ viewMatrix: viewMatrix2,
2434
+ longitude,
2435
+ latitude,
2436
+ zoom,
2437
+ // projection matrix parameters
2438
+ distanceScales: getDistanceScales2(),
2439
+ fovy,
2440
+ focalDistance: altitude,
2441
+ near: nearZ,
2442
+ far: farZ
2443
+ });
2444
+ this.scale = scale;
2445
+ this.latitude = latitude;
2446
+ this.longitude = longitude;
2447
+ this.bearing = bearing;
2448
+ this.pitch = pitch;
2449
+ this.fovy = fovy;
2450
+ this.resolution = resolution;
2451
+ }
2452
+ get projectionMode() {
2453
+ return PROJECTION_MODE.GLOBE;
2454
+ }
2455
+ getDistanceScales() {
2456
+ return this.distanceScales;
2457
+ }
2458
+ getBounds(options = {}) {
2459
+ const unprojectOption = { targetZ: options.z || 0 };
2460
+ const left = this.unproject([0, this.height / 2], unprojectOption);
2461
+ const top = this.unproject([this.width / 2, 0], unprojectOption);
2462
+ const right = this.unproject([this.width, this.height / 2], unprojectOption);
2463
+ const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
2464
+ if (right[0] < this.longitude) right[0] += 360;
2465
+ if (left[0] > this.longitude) left[0] -= 360;
2466
+ return [
2467
+ Math.min(left[0], right[0], top[0], bottom[0]),
2468
+ Math.min(left[1], right[1], top[1], bottom[1]),
2469
+ Math.max(left[0], right[0], top[0], bottom[0]),
2470
+ Math.max(left[1], right[1], top[1], bottom[1])
2471
+ ];
2472
+ }
2473
+ /**
2474
+ * Builds the screen-pixel → globe-center ray and the intermediate ray/sphere
2475
+ * math reused by `unproject` and anchored zoom. One function so the same
2476
+ * pixelUnprojectionMatrix work isn't duplicated.
2477
+ */
2478
+ _getRayToGlobe(screenPosition, { topLeft = true, targetZ } = {}) {
2479
+ const [screenX, screenY] = screenPosition;
2480
+ const adjustedScreenY = topLeft ? screenY : this.height - screenY;
2481
+ const { pixelUnprojectionMatrix } = this;
2482
+ const rayStartPosition = transformVector(pixelUnprojectionMatrix, [
2483
+ screenX,
2484
+ adjustedScreenY,
2485
+ -1,
2486
+ 1
2487
+ ]);
2488
+ const rayEndPosition = transformVector(pixelUnprojectionMatrix, [
2489
+ screenX,
2490
+ adjustedScreenY,
2491
+ 1,
2492
+ 1
2493
+ ]);
2494
+ const radius = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
2495
+ const rayLengthSquared = import_core6.vec3.sqrLen(import_core6.vec3.sub([], rayStartPosition, rayEndPosition));
2496
+ const rayStartDistanceSquared = import_core6.vec3.sqrLen(rayStartPosition);
2497
+ const rayEndDistanceSquared = import_core6.vec3.sqrLen(rayEndPosition);
2498
+ const triangleAreaSquared = (4 * rayStartDistanceSquared * rayEndDistanceSquared - (rayLengthSquared - rayStartDistanceSquared - rayEndDistanceSquared) ** 2) / 16;
2499
+ const distanceToCenterSquared = 4 * triangleAreaSquared / rayLengthSquared;
2500
+ return {
2501
+ rayStartPosition,
2502
+ rayEndPosition,
2503
+ radius,
2504
+ rayLengthSquared,
2505
+ rayStartDistanceSquared,
2506
+ distanceToCenterSquared
2507
+ };
2508
+ }
2509
+ _getRayDistanceToGlobeCenterRatio(screenPosition, options) {
2510
+ const { distanceToCenterSquared, radius } = this._getRayToGlobe(screenPosition, options);
2511
+ return Math.sqrt(Math.max(0, distanceToCenterSquared)) / radius;
2512
+ }
2513
+ /**
2514
+ * Returns how strongly a screen position should anchor zoom on the visible globe.
2515
+ * A value of `0` means that the controller should fall back to center zoom.
2516
+ * @param screenPosition - Screen position to evaluate.
2517
+ * @returns Anchor strength from `0` to `1`.
2518
+ */
2519
+ getZoomAnchorStrength(screenPosition) {
2520
+ const distanceRatio = this._getRayDistanceToGlobeCenterRatio(screenPosition);
2521
+ if (distanceRatio >= GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO) {
2522
+ return 0;
2523
+ }
2524
+ const edgeProgress = Math.max(
2525
+ 0,
2526
+ Math.min(
2527
+ 1,
2528
+ (distanceRatio - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO) / (GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO)
2529
+ )
2530
+ );
2531
+ const smoothProgress = edgeProgress * edgeProgress * (3 - 2 * edgeProgress);
2532
+ return 1 - smoothProgress;
2533
+ }
2534
+ unproject(xyz, { topLeft = true, targetZ } = {}) {
2535
+ const [x, y, z] = xyz;
2536
+ const y2 = topLeft ? y : this.height - y;
2537
+ const { pixelUnprojectionMatrix } = this;
2538
+ let coord;
2539
+ if (Number.isFinite(z)) {
2540
+ coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
2541
+ } else {
2542
+ const {
2543
+ rayStartPosition,
2544
+ rayEndPosition,
2545
+ radius,
2546
+ rayLengthSquared,
2547
+ rayStartDistanceSquared,
2548
+ distanceToCenterSquared
2549
+ } = this._getRayToGlobe(xyz, { topLeft, targetZ });
2550
+ const rayStartToClosestApproach = Math.sqrt(
2551
+ rayStartDistanceSquared - distanceToCenterSquared
2552
+ );
2553
+ const closestApproachToIntersection = Math.sqrt(
2554
+ Math.max(0, radius * radius - distanceToCenterSquared)
2555
+ );
2556
+ const intersectionRatio = (rayStartToClosestApproach - closestApproachToIntersection) / Math.sqrt(rayLengthSquared);
2557
+ coord = import_core6.vec3.lerp([], rayStartPosition, rayEndPosition, intersectionRatio);
2558
+ }
2559
+ const [X, Y, Z] = this.unprojectPosition(coord);
2560
+ if (Number.isFinite(z)) {
2561
+ return [X, Y, Z];
2562
+ }
2563
+ return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
2564
+ }
2565
+ projectPosition(xyz) {
2566
+ const [lng, lat, Z = 0] = xyz;
2567
+ const lambda = lng * DEGREES_TO_RADIANS2;
2568
+ const phi = lat * DEGREES_TO_RADIANS2;
2569
+ const cosPhi = Math.cos(phi);
2570
+ const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
2571
+ return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
2572
+ }
2573
+ unprojectPosition(xyz) {
2574
+ const [x, y, z] = xyz;
2575
+ const D = import_core6.vec3.len(xyz);
2576
+ const phi = Math.asin(z / D);
2577
+ const lambda = Math.atan2(x, -y);
2578
+ const lng = lambda * RADIANS_TO_DEGREES;
2579
+ const lat = phi * RADIANS_TO_DEGREES;
2580
+ const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
2581
+ return [lng, lat, Z];
2582
+ }
2583
+ projectFlat(xyz) {
2584
+ return xyz;
2585
+ }
2586
+ unprojectFlat(xyz) {
2587
+ return xyz;
2588
+ }
2589
+ /**
2590
+ * Pan the globe to place geographic coordinates at a screen pixel.
2591
+ * When `dragStartPosition` is supplied, applies the delta-based movement used by globe dragging.
2592
+ * @param coordinates - Geographic anchor, or the starting longitude, latitude and zoom.
2593
+ * @param screenPosition - Current screen position.
2594
+ * @param dragStartPosition - Screen position where a drag started.
2595
+ * @returns Updated viewport options.
2596
+ */
2597
+ panByPosition(coordinates, screenPosition, dragStartPosition) {
2598
+ if (!dragStartPosition) {
2599
+ let anchorStrength = this.getZoomAnchorStrength(screenPosition);
2600
+ if (anchorStrength === 0) {
2601
+ return { longitude: this.longitude, latitude: this.latitude };
2602
+ }
2603
+ const currentCoordinates = this.unproject(screenPosition);
2604
+ const longitudeDelta = mod(coordinates[0] - currentCoordinates[0] + 180, 360) - 180;
2605
+ const latitudeDelta = coordinates[1] - currentCoordinates[1];
2606
+ const crossesPole = Math.abs(currentCoordinates[1]) > import_web_mercator4.MAX_LATITUDE || Math.abs(longitudeDelta) > 90;
2607
+ if (isGlobeNorthUp(this.bearing) && crossesPole) {
2608
+ return { longitude: this.longitude, latitude: this.latitude };
2609
+ }
2610
+ if (isGlobeNorthUp(this.bearing) && latitudeDelta !== 0) {
2611
+ const latitudeLimit = latitudeDelta > 0 ? import_web_mercator4.MAX_LATITUDE : -import_web_mercator4.MAX_LATITUDE;
2612
+ const latitudeConstraintStrength = (latitudeLimit - this.latitude) / latitudeDelta;
2613
+ anchorStrength = Math.min(anchorStrength, Math.max(0, latitudeConstraintStrength));
2614
+ }
2615
+ const longitude2 = this.longitude + longitudeDelta * anchorStrength;
2616
+ const latitude2 = Math.max(Math.min(this.latitude + latitudeDelta * anchorStrength, 90), -90);
2617
+ return { longitude: longitude2, latitude: latitude2 };
2618
+ }
2619
+ const [startLongitude, startLatitude, startZoom] = coordinates;
2620
+ const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
2621
+ const rotationSpeed = 0.25 / scale;
2622
+ const longitude = startLongitude + rotationSpeed * (dragStartPosition[0] - screenPosition[0]);
2623
+ let latitude = startLatitude - rotationSpeed * (dragStartPosition[1] - screenPosition[1]);
2624
+ latitude = Math.max(Math.min(latitude, 90), -90);
2625
+ const nextViewState = { longitude, latitude, zoom: startZoom - zoomAdjust(startLatitude) };
2626
+ nextViewState.zoom += zoomAdjust(nextViewState.latitude);
2627
+ return nextViewState;
2628
+ }
2629
+ };
2630
+ GlobeViewport.displayName = "GlobeViewport";
2631
+ function zoomAdjust(latitude, clampToPoles) {
2632
+ if (clampToPoles) {
2633
+ latitude = Math.max(Math.min(latitude, import_web_mercator4.MAX_LATITUDE), -import_web_mercator4.MAX_LATITUDE);
2634
+ }
2635
+ const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
2636
+ return Math.log2(scaleAdjust);
2637
+ }
2638
+ function transformVector(matrix, vector) {
2639
+ const result = import_core6.vec4.transformMat4([], vector, matrix);
2640
+ import_core6.vec4.scale(result, result, 1 / result[3]);
2641
+ return result;
2642
+ }
2643
+
2644
+ // ../core/src/viewports/orthographic-viewport.ts
2645
+ var import_core7 = require("@math.gl/core");
2646
+ var import_web_mercator5 = require("@math.gl/web-mercator");
2647
+ var viewMatrix = new import_core7.Matrix4().lookAt({ eye: [0, 0, 1] });
2648
+ function getProjectionMatrix({
2649
+ width,
2650
+ height,
2651
+ near,
2652
+ far,
2653
+ padding
2654
+ }) {
2655
+ let left = -width / 2;
2656
+ let right = width / 2;
2657
+ let bottom = -height / 2;
2658
+ let top = height / 2;
2659
+ if (padding) {
2660
+ const { left: l = 0, right: r = 0, top: t = 0, bottom: b = 0 } = padding;
2661
+ const offsetX = (0, import_core7.clamp)((l + width - r) / 2, 0, width) - width / 2;
2662
+ const offsetY = (0, import_core7.clamp)((t + height - b) / 2, 0, height) - height / 2;
2663
+ left -= offsetX;
2664
+ right -= offsetX;
2665
+ bottom += offsetY;
2666
+ top += offsetY;
2667
+ }
2668
+ return new import_core7.Matrix4().ortho({
2669
+ left,
2670
+ right,
2671
+ bottom,
2672
+ top,
2673
+ near,
2674
+ far
2675
+ });
2676
+ }
2677
+ var OrthographicViewport = class extends Viewport {
2678
+ constructor(props) {
2679
+ const {
2680
+ width,
2681
+ height,
2682
+ near = 0.1,
2683
+ far = 1e3,
2684
+ zoom = 0,
2685
+ target = [0, 0, 0],
2686
+ padding = null,
2687
+ flipY = true
2688
+ } = props;
2689
+ const zoomX = props.zoomX ?? (Array.isArray(zoom) ? zoom[0] : zoom);
2690
+ const zoomY = props.zoomY ?? (Array.isArray(zoom) ? zoom[1] : zoom);
2691
+ const zoom_ = Number.isFinite(props.zoom) ? props.zoom : Math.min(zoomX, zoomY);
2692
+ const scale = Math.pow(2, zoom_);
2693
+ let distanceScales;
2694
+ if (zoomX !== zoom_ || zoomY !== zoom_) {
2695
+ const scaleX = Math.pow(2, zoomX);
2696
+ const scaleY = Math.pow(2, zoomY);
2697
+ distanceScales = {
2698
+ unitsPerMeter: [scaleX / scale, scaleY / scale, 1],
2699
+ metersPerUnit: [scale / scaleX, scale / scaleY, 1]
2700
+ };
2701
+ }
2702
+ super({
2703
+ ...props,
2704
+ // in case viewState contains longitude/latitude values,
2705
+ // make sure that the base Viewport class does not treat this as a geospatial viewport
2706
+ longitude: void 0,
2707
+ position: target,
2708
+ viewMatrix: viewMatrix.clone().scale([scale, scale * (flipY ? -1 : 1), scale]),
2709
+ projectionMatrix: getProjectionMatrix({
2710
+ width: width || 1,
2711
+ height: height || 1,
2712
+ padding,
2713
+ near,
2714
+ far
2715
+ }),
2716
+ zoom: zoom_,
2717
+ distanceScales
2718
+ });
2719
+ this.target = target;
2720
+ this.zoomX = zoomX;
2721
+ this.zoomY = zoomY;
2722
+ this.flipY = flipY;
2723
+ }
2724
+ projectFlat([X, Y]) {
2725
+ const { unitsPerMeter: unitsPerMeter2 } = this.distanceScales;
2726
+ return [X * unitsPerMeter2[0], Y * unitsPerMeter2[1]];
2727
+ }
2728
+ unprojectFlat([x, y]) {
2729
+ const { metersPerUnit } = this.distanceScales;
2730
+ return [x * metersPerUnit[0], y * metersPerUnit[1]];
2731
+ }
2732
+ /* Needed by LinearInterpolator */
2733
+ panByPosition(coords, pixel, startPixel) {
2734
+ const fromLocation = (0, import_web_mercator5.pixelsToWorld)(pixel, this.pixelUnprojectionMatrix);
2735
+ const toLocation = this.projectFlat(coords);
2736
+ const translate = import_core7.vec2.add([], toLocation, import_core7.vec2.negate([], fromLocation));
2737
+ const newCenter = import_core7.vec2.add([], this.center, translate);
2738
+ return { target: this.unprojectFlat(newCenter) };
2739
+ }
2740
+ };
2741
+ OrthographicViewport.displayName = "OrthographicViewport";
2742
+
2743
+ // ../core/src/controllers/orthographic-controller.ts
2744
+ var import_core8 = require("@math.gl/core");
2745
+ var ZOOM_RUBBER_BAND_RANGE = 1;
2746
+ function normalizeZoom({
2747
+ zoom = 0,
2748
+ zoomX,
2749
+ zoomY
2750
+ }) {
2751
+ zoomX = zoomX ?? (Array.isArray(zoom) ? zoom[0] : zoom);
2752
+ zoomY = zoomY ?? (Array.isArray(zoom) ? zoom[1] : zoom);
2753
+ return { zoomX, zoomY };
2754
+ }
2755
+ function getAxisBounds(maxBounds, index, negativeExtent, positiveExtent, target) {
2756
+ const minimum = maxBounds[0][index] + negativeExtent;
2757
+ const maximum = maxBounds[1][index] - positiveExtent;
2758
+ const midpoint = (minimum + maximum) / 2;
2759
+ return {
2760
+ minimum,
2761
+ maximum,
2762
+ midpoint,
2763
+ settledTarget: Number.isFinite(negativeExtent) && Number.isFinite(positiveExtent) && minimum <= maximum ? (0, import_core8.clamp)(target, minimum, maximum) : midpoint
2764
+ };
2765
+ }
2766
+ var OrthographicState = class extends ViewState {
2767
+ constructor(options) {
2768
+ const {
2769
+ /* Viewport arguments */
2770
+ width,
2771
+ // Width of viewport
2772
+ height,
2773
+ // Height of viewport
2774
+ target = [0, 0, 0],
2775
+ zoom = 0,
2776
+ zoomAxis = "all",
2777
+ /* Viewport constraints */
2778
+ minZoom = -Infinity,
2779
+ maxZoom = Infinity,
2780
+ minZoomX = minZoom,
2781
+ maxZoomX = maxZoom,
2782
+ minZoomY = minZoom,
2783
+ maxZoomY = maxZoom,
2784
+ maxBounds = null,
2785
+ maxBoundsPadding = null,
2786
+ rubberBand = false,
2787
+ /** Interaction states, required to calculate change during transform */
2788
+ // Model state when the pan operation first started
2789
+ startPanPosition,
2790
+ // Model state when the zoom operation first started
2791
+ startZoomPosition,
2792
+ startZoom
2793
+ } = options;
2794
+ const { [CONSTRAINT_AROUND]: constraintAround } = options;
2795
+ const { zoomX, zoomY } = normalizeZoom(options);
2796
+ super(
2797
+ {
2798
+ width,
2799
+ height,
2800
+ target,
2801
+ zoom,
2802
+ zoomX,
2803
+ zoomY,
2804
+ zoomAxis,
2805
+ minZoomX,
2806
+ maxZoomX,
2807
+ minZoomY,
2808
+ maxZoomY,
2809
+ maxBounds,
2810
+ maxBoundsPadding,
2811
+ rubberBand,
2812
+ ...{ [CONSTRAINT_AROUND]: constraintAround }
2813
+ },
2814
+ {
2815
+ startPanPosition,
2816
+ startZoomPosition,
2817
+ startZoom
2818
+ },
2819
+ options.makeViewport,
2820
+ options.constraintContext
2821
+ );
2822
+ }
2823
+ /**
2824
+ * Start panning
2825
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
2826
+ */
2827
+ panStart({ pos }, constraintContext) {
2828
+ return this._getUpdatedState({ startPanPosition: this._unproject(pos) }, constraintContext);
2829
+ }
2830
+ /**
2831
+ * Pan
2832
+ * @param {[Number, Number]} pos - position on screen where the pointer is
2833
+ */
2834
+ pan({ pos, startPosition }, constraintContext) {
2835
+ const startPanPosition = this.getState().startPanPosition || startPosition;
2836
+ if (!startPanPosition) {
2837
+ return this;
2838
+ }
2839
+ const viewport = this.makeViewport(this.getViewportProps());
2840
+ const newProps = viewport.panByPosition(startPanPosition, pos);
2841
+ return this._getUpdatedState(newProps, constraintContext);
2842
+ }
2843
+ /**
2844
+ * End panning
2845
+ * Must call if `panStart()` was called
2846
+ */
2847
+ panEnd(constraintContext) {
2848
+ return this._getUpdatedState({ startPanPosition: null }, constraintContext);
2849
+ }
2850
+ /**
2851
+ * Start rotating
2852
+ */
2853
+ rotateStart() {
2854
+ return this;
2855
+ }
2856
+ /**
2857
+ * Rotate
2858
+ */
2859
+ rotate() {
2860
+ return this;
2861
+ }
2862
+ /**
2863
+ * End rotating
2864
+ */
2865
+ rotateEnd() {
2866
+ return this;
2867
+ }
2868
+ // shortest path between two view states
2869
+ shortestPathFrom(viewState) {
2870
+ const fromProps = viewState.getViewportProps();
2871
+ const props = { ...this.getViewportProps() };
2872
+ return props;
2873
+ }
2874
+ /**
2875
+ * Start zooming
2876
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
2877
+ */
2878
+ zoomStart({ pos }, constraintContext) {
2879
+ const { zoomX, zoomY } = this.getViewportProps();
2880
+ return this._getUpdatedState(
2881
+ {
2882
+ startZoomPosition: this._unproject(pos),
2883
+ startZoom: [zoomX, zoomY]
2884
+ },
2885
+ constraintContext
2886
+ );
2887
+ }
2888
+ /**
2889
+ * Zoom
2890
+ * @param {[Number, Number]} pos - position on screen where the current target is
2891
+ * @param {[Number, Number]} startPos - the target position at
2892
+ * the start of the operation. Must be supplied of `zoomStart()` was not called
2893
+ * @param {Number} scale - a number between [0, 1] specifying the accumulated
2894
+ * relative scale.
2895
+ */
2896
+ zoom({
2897
+ pos,
2898
+ startPos,
2899
+ scale
2900
+ }, constraintContext) {
2901
+ let { startZoom, startZoomPosition } = this.getState();
2902
+ if (!startZoomPosition) {
2903
+ const { zoomX, zoomY } = this.getViewportProps();
2904
+ startZoom = [zoomX, zoomY];
2905
+ startZoomPosition = this._unproject(startPos || pos);
2906
+ }
2907
+ if (!startZoomPosition) {
2908
+ return this;
2909
+ }
2910
+ const newZoomProps = this._calculateNewZoom({ scale, startZoom });
2911
+ return this._getUpdatedState(
2912
+ {
2913
+ ...newZoomProps,
2914
+ [CONSTRAINT_AROUND]: { position: startZoomPosition, screenPosition: pos }
2915
+ },
2916
+ constraintContext
2917
+ );
2918
+ }
2919
+ /**
2920
+ * End zooming
2921
+ * Must call if `zoomStart()` was called
2922
+ */
2923
+ zoomEnd(constraintContext) {
2924
+ return this._getUpdatedState(
2925
+ {
2926
+ startZoomPosition: null,
2927
+ startZoom: null
2928
+ },
2929
+ constraintContext
2930
+ );
2931
+ }
2932
+ zoomIn(speed = 2, constraintContext) {
2933
+ return this._getUpdatedState(this._calculateNewZoom({ scale: speed }), constraintContext);
2934
+ }
2935
+ zoomOut(speed = 2, constraintContext) {
2936
+ return this._getUpdatedState(this._calculateNewZoom({ scale: 1 / speed }), constraintContext);
2937
+ }
2938
+ moveLeft(speed = 50, constraintContext) {
2939
+ return this._panFromCenter([-speed, 0], constraintContext);
2940
+ }
2941
+ moveRight(speed = 50, constraintContext) {
2942
+ return this._panFromCenter([speed, 0], constraintContext);
2943
+ }
2944
+ moveUp(speed = 50, constraintContext) {
2945
+ return this._panFromCenter([0, -speed], constraintContext);
2946
+ }
2947
+ moveDown(speed = 50, constraintContext) {
2948
+ return this._panFromCenter([0, speed], constraintContext);
2949
+ }
2950
+ rotateLeft(speed = 15) {
2951
+ return this;
2952
+ }
2953
+ rotateRight(speed = 15) {
2954
+ return this;
2955
+ }
2956
+ rotateUp(speed = 10) {
2957
+ return this;
2958
+ }
2959
+ rotateDown(speed = 10) {
2960
+ return this;
2961
+ }
2962
+ /* Private methods */
2963
+ _project(pos) {
2964
+ const viewport = this.makeViewport(this.getViewportProps());
2965
+ return viewport.project(pos);
2966
+ }
2967
+ _unproject(pos) {
2968
+ const viewport = this.makeViewport(this.getViewportProps());
2969
+ const [x, y] = viewport.unproject(pos);
2970
+ return [x, y];
2971
+ }
2972
+ // Calculates new zoom
2973
+ _calculateNewZoom({ scale, startZoom }) {
2974
+ const { zoomX, zoomY, zoomAxis } = this.getViewportProps();
2975
+ if (startZoom === void 0) {
2976
+ startZoom = [zoomX, zoomY];
2977
+ }
2978
+ const deltaZoom = Math.log2(scale);
2979
+ let [newZoomX, newZoomY] = startZoom;
2980
+ switch (zoomAxis) {
2981
+ case "X":
2982
+ newZoomX += deltaZoom;
2983
+ break;
2984
+ case "Y":
2985
+ newZoomY += deltaZoom;
2986
+ break;
2987
+ default:
2988
+ newZoomX += deltaZoom;
2989
+ newZoomY += deltaZoom;
2990
+ }
2991
+ return {
2992
+ zoomX: newZoomX,
2993
+ zoomY: newZoomY
2994
+ };
2995
+ }
2996
+ _panFromCenter(offset3, constraintContext) {
2997
+ const { target } = this.getViewportProps();
2998
+ const center = this._project(target);
2999
+ return this.pan(
3000
+ {
3001
+ startPosition: target,
3002
+ pos: [center[0] + offset3[0], center[1] + offset3[1]]
3003
+ },
3004
+ constraintContext
3005
+ );
3006
+ }
3007
+ _getUpdatedState(newProps, constraintContext) {
3008
+ return new this.constructor({
3009
+ makeViewport: this.makeViewport,
3010
+ ...this.getViewportProps(),
3011
+ ...this.getState(),
3012
+ ...newProps,
3013
+ constraintContext
3014
+ });
3015
+ }
3016
+ // Apply any constraints (mathematical or defined by _viewportProps) to map state
3017
+ applyConstraints(props, constraintContext) {
3018
+ const internalProps = props;
3019
+ const constraintAround = internalProps[CONSTRAINT_AROUND];
3020
+ delete internalProps[CONSTRAINT_AROUND];
3021
+ const normalizedZoom = normalizeZoom(props);
3022
+ const constrainedZoom = this._constrainZoom(normalizedZoom, props);
3023
+ const shouldRubberBand = props.rubberBand && (constraintContext == null ? void 0 : constraintContext.mode) === "elastic";
3024
+ const { zoomX, zoomY } = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? normalizedZoom : shouldRubberBand ? {
3025
+ zoomX: applyRubberBand(
3026
+ normalizedZoom.zoomX,
3027
+ constrainedZoom.zoomX,
3028
+ ZOOM_RUBBER_BAND_RANGE
3029
+ ),
3030
+ zoomY: applyRubberBand(
3031
+ normalizedZoom.zoomY,
3032
+ constrainedZoom.zoomY,
3033
+ ZOOM_RUBBER_BAND_RANGE
3034
+ )
3035
+ } : constrainedZoom;
3036
+ props.zoomX = zoomX;
3037
+ props.zoomY = zoomY;
3038
+ if (constraintAround) {
3039
+ const viewport = this.makeViewport({ ...props, zoomX, zoomY });
3040
+ Object.assign(
3041
+ props,
3042
+ viewport.panByPosition(constraintAround.position, constraintAround.screenPosition)
3043
+ );
3044
+ }
3045
+ props.zoom = Array.isArray(props.zoom) || props.zoomX !== props.zoomY ? [props.zoomX, props.zoomY] : props.zoomX;
3046
+ const { maxBounds, rubberBand, target } = props;
3047
+ if (maxBounds) {
3048
+ const maxBoundsRect = getMaxBoundsRect(props.width, props.height, props.maxBoundsPadding);
3049
+ const viewport = this.makeViewport(props);
3050
+ const screenExtents = getMaxBoundsExtents(viewport, target, maxBoundsRect);
3051
+ const projectedTarget = viewport.project(target);
3052
+ const pixelsPerUnit = [0, 1].map((index) => {
3053
+ const sampleTarget = target.slice();
3054
+ sampleTarget[index] += 1;
3055
+ const projectedDelta = viewport.project(sampleTarget)[index] - projectedTarget[index];
3056
+ return Number.isFinite(projectedDelta) ? projectedDelta : 2 ** (index === 0 ? zoomX : zoomY) * (index === 0 ? 1 : -1);
3057
+ });
3058
+ const worldExtents = pixelsPerUnit.map((projectedDelta, index) => {
3059
+ const negativeScreenExtent = index === 0 ? screenExtents.left : screenExtents.top;
3060
+ const positiveScreenExtent = index === 0 ? screenExtents.right : screenExtents.bottom;
3061
+ const scale = Math.abs(projectedDelta);
3062
+ return projectedDelta >= 0 ? [negativeScreenExtent / scale, positiveScreenExtent / scale] : [positiveScreenExtent / scale, negativeScreenExtent / scale];
3063
+ });
3064
+ const constrainedTarget = target.slice();
3065
+ const targetDimensions = [maxBoundsRect.width, maxBoundsRect.height];
3066
+ for (const [index, [negativeExtent, positiveExtent]] of worldExtents.entries()) {
3067
+ if (targetDimensions[index] < 0) {
3068
+ continue;
3069
+ }
3070
+ const { minimum, maximum, midpoint, settledTarget } = getAxisBounds(
3071
+ maxBounds,
3072
+ index,
3073
+ negativeExtent,
3074
+ positiveExtent,
3075
+ target[index]
3076
+ );
3077
+ if ((constraintContext == null ? void 0 : constraintContext.mode) !== "preserve" && rubberBand && (!Number.isFinite(negativeExtent) || !Number.isFinite(positiveExtent))) {
3078
+ constrainedTarget[index] = midpoint;
3079
+ continue;
3080
+ }
3081
+ const constrained = rubberBand ? settledTarget : (0, import_core8.clamp)(target[index], minimum, maximum);
3082
+ constrainedTarget[index] = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? target[index] : shouldRubberBand ? applyRubberBand(
3083
+ target[index],
3084
+ constrained,
3085
+ (index === 0 ? maxBoundsRect.width : maxBoundsRect.height) / 2 / Math.abs(pixelsPerUnit[index])
3086
+ ) : constrained;
3087
+ }
3088
+ if (constrainedTarget[0] !== target[0] || constrainedTarget[1] !== target[1]) {
3089
+ props.target = constrainedTarget;
3090
+ }
3091
+ }
3092
+ return props;
3093
+ }
3094
+ _constrainZoom({ zoomX, zoomY }, props) {
3095
+ props ||= this.getViewportProps();
3096
+ const { zoomAxis, maxZoomX, maxZoomY, maxBounds } = props;
3097
+ let { minZoomX, minZoomY } = props;
3098
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
3099
+ if (shouldApplyMaxBounds) {
3100
+ const maxBoundsRect = getMaxBoundsRect(props.width, props.height, props.maxBoundsPadding);
3101
+ const bl = maxBounds[0];
3102
+ const tr = maxBounds[1];
3103
+ const w = tr[0] - bl[0];
3104
+ const h = tr[1] - bl[1];
3105
+ if (maxBoundsRect.width > 0 && Number.isFinite(w) && w > 0) {
3106
+ minZoomX = Math.max(minZoomX, Math.log2(maxBoundsRect.width / w));
3107
+ if (minZoomX > maxZoomX) minZoomX = maxZoomX;
3108
+ }
3109
+ if (maxBoundsRect.height > 0 && Number.isFinite(h) && h > 0) {
3110
+ minZoomY = Math.max(minZoomY, Math.log2(maxBoundsRect.height / h));
3111
+ if (minZoomY > maxZoomY) minZoomY = maxZoomY;
3112
+ }
3113
+ }
3114
+ switch (zoomAxis) {
3115
+ case "X":
3116
+ zoomX = (0, import_core8.clamp)(zoomX, minZoomX, maxZoomX);
3117
+ break;
3118
+ case "Y":
3119
+ zoomY = (0, import_core8.clamp)(zoomY, minZoomY, maxZoomY);
3120
+ break;
3121
+ default:
3122
+ let delta = Math.min(maxZoomX - zoomX, maxZoomY - zoomY, 0);
3123
+ if (delta === 0) {
3124
+ delta = Math.max(minZoomX - zoomX, minZoomY - zoomY, 0);
3125
+ }
3126
+ if (delta !== 0) {
3127
+ zoomX += delta;
3128
+ zoomY += delta;
3129
+ }
3130
+ }
3131
+ return { zoomX, zoomY };
3132
+ }
3133
+ };
3134
+ var OrthographicController = class extends Controller {
3135
+ constructor() {
3136
+ super(...arguments);
3137
+ this.ControllerState = OrthographicState;
3138
+ this.transition = {
3139
+ transitionDuration: 300,
3140
+ transitionInterpolator: new LinearInterpolator(["target", "zoomX", "zoomY"])
3141
+ };
3142
+ this.dragMode = "pan";
3143
+ }
3144
+ setProps(props) {
3145
+ Object.assign(props, normalizeZoom(props));
3146
+ super.setProps(props);
3147
+ }
3148
+ _onMultiPanStart(event) {
3149
+ return this.multiTouchDrag === "pan" && super._onMultiPanStart(event);
3150
+ }
3151
+ _onPanRotate() {
3152
+ return false;
3153
+ }
3154
+ };
3155
+
3156
+ // ../core/src/views/orthographic-view.ts
3157
+ var OrthographicView = class extends View {
3158
+ constructor(props = {}) {
3159
+ super(props);
3160
+ }
3161
+ getViewportType() {
3162
+ return OrthographicViewport;
3163
+ }
3164
+ get ControllerType() {
3165
+ return OrthographicController;
3166
+ }
3167
+ };
3168
+ OrthographicView.displayName = "OrthographicView";
3169
+
3170
+ // ../core/src/transitions/fly-to-interpolator.ts
3171
+ var import_core9 = require("@math.gl/core");
3172
+ var import_web_mercator6 = require("@math.gl/web-mercator");
3173
+ var LINEARLY_INTERPOLATED_PROPS = {
3174
+ bearing: 0,
3175
+ pitch: 0,
3176
+ position: [0, 0, 0]
3177
+ };
3178
+ var DEFAULT_OPTS = {
3179
+ speed: 1.2,
3180
+ curve: 1.414
3181
+ };
3182
+ var FlyToInterpolator = class extends TransitionInterpolator {
3183
+ constructor(opts = {}) {
3184
+ super({
3185
+ compare: ["longitude", "latitude", "zoom", "bearing", "pitch", "position"],
3186
+ extract: ["width", "height", "longitude", "latitude", "zoom", "bearing", "pitch", "position"],
3187
+ required: ["width", "height", "latitude", "longitude", "zoom"]
3188
+ });
3189
+ this.opts = { ...DEFAULT_OPTS, ...opts };
3190
+ }
3191
+ interpolateProps(startProps, endProps, t) {
3192
+ const viewport = (0, import_web_mercator6.flyToViewport)(startProps, endProps, t, this.opts);
3193
+ for (const key in LINEARLY_INTERPOLATED_PROPS) {
3194
+ viewport[key] = (0, import_core9.lerp)(
3195
+ startProps[key] || LINEARLY_INTERPOLATED_PROPS[key],
3196
+ endProps[key] || LINEARLY_INTERPOLATED_PROPS[key],
3197
+ t
3198
+ );
3199
+ }
3200
+ return viewport;
3201
+ }
3202
+ // computes the transition duration
3203
+ getDuration(startProps, endProps) {
3204
+ let { transitionDuration } = endProps;
3205
+ if (transitionDuration === "auto") {
3206
+ transitionDuration = (0, import_web_mercator6.getFlyToDuration)(startProps, endProps, this.opts);
3207
+ }
3208
+ return transitionDuration;
3209
+ }
3210
+ };
60
3211
 
61
3212
  // dist/zoom-widget.js
62
- var import_jsx_runtime3 = require("preact/jsx-runtime");
63
- var import_core = require("@deck.gl/core");
64
3213
  var import_preact = require("preact");
65
3214
 
66
3215
  // dist/lib/components/button-group.js
@@ -71,8 +3220,8 @@ var ButtonGroup = (props) => {
71
3220
  };
72
3221
 
73
3222
  // dist/lib/components/icon-button.js
74
- var import_jsx_runtime2 = require("preact/jsx-runtime");
75
- var import_hooks = require("preact/hooks");
3223
+ var import_jsx_runtime3 = require("preact/jsx-runtime");
3224
+ var import_hooks2 = require("preact/hooks");
76
3225
 
77
3226
  // dist/lib/data-url.js
78
3227
  function getCSSMask(imageUrl) {
@@ -82,20 +3231,87 @@ function getCSSMask(imageUrl) {
82
3231
  return { maskImage: cssUrl, WebkitMaskImage: cssUrl };
83
3232
  }
84
3233
 
3234
+ // dist/lib/components/tooltip.js
3235
+ var import_jsx_runtime2 = require("preact/jsx-runtime");
3236
+ var import_hooks = require("preact/hooks");
3237
+ var import_dom = require("@floating-ui/dom");
3238
+ var Tooltip = ({ content, placement = "right", children }) => {
3239
+ const wrapperRef = (0, import_hooks.useRef)(null);
3240
+ const tooltipRef = (0, import_hooks.useRef)(null);
3241
+ const [visible, setVisible] = (0, import_hooks.useState)(false);
3242
+ const delayRef = (0, import_hooks.useRef)();
3243
+ const show = (0, import_hooks.useCallback)(() => {
3244
+ clearTimeout(delayRef.current);
3245
+ setVisible(true);
3246
+ }, []);
3247
+ const hide = (0, import_hooks.useCallback)(() => {
3248
+ clearTimeout(delayRef.current);
3249
+ setVisible(false);
3250
+ }, []);
3251
+ const onKeyDown = (0, import_hooks.useCallback)((e) => {
3252
+ if (e.key === "Escape")
3253
+ hide();
3254
+ }, [hide]);
3255
+ (0, import_hooks.useEffect)(() => {
3256
+ var _a;
3257
+ const tooltip = tooltipRef.current;
3258
+ const trigger = (_a = wrapperRef.current) == null ? void 0 : _a.firstElementChild;
3259
+ if (!visible || !tooltip || !trigger)
3260
+ return void 0;
3261
+ const updatePosition = () => {
3262
+ (0, import_dom.computePosition)(trigger, tooltip, {
3263
+ placement,
3264
+ strategy: "fixed",
3265
+ middleware: [(0, import_dom.offset)(8), (0, import_dom.flip)(), (0, import_dom.shift)({ padding: 4 })]
3266
+ }).then(({ x, y }) => {
3267
+ if (tooltipRef.current === tooltip) {
3268
+ Object.assign(tooltip.style, { left: `${x}px`, top: `${y}px`, opacity: "1" });
3269
+ }
3270
+ });
3271
+ };
3272
+ return (0, import_dom.autoUpdate)(trigger, tooltip, updatePosition);
3273
+ }, [visible, placement]);
3274
+ (0, import_hooks.useEffect)(() => {
3275
+ return () => clearTimeout(delayRef.current);
3276
+ }, []);
3277
+ const htmlRef = (0, import_hooks.useCallback)((el) => {
3278
+ if (el && content instanceof HTMLElement) {
3279
+ el.replaceChildren(content.cloneNode(true));
3280
+ }
3281
+ }, [content]);
3282
+ const renderContent = () => {
3283
+ if (content instanceof HTMLElement) {
3284
+ return (0, import_jsx_runtime2.jsx)("div", { ref: htmlRef });
3285
+ }
3286
+ return content;
3287
+ };
3288
+ if (content === null || content === void 0 || content === false || content === "") {
3289
+ return (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
3290
+ }
3291
+ return (0, import_jsx_runtime2.jsxs)("div", { ref: wrapperRef, className: "deck-widget-tooltip-trigger", onPointerEnter: show, onPointerLeave: hide, onFocusCapture: show, onBlurCapture: hide, onKeyDown, children: [children, visible && (0, import_jsx_runtime2.jsx)("div", { ref: tooltipRef, className: "deck-widget-tooltip", role: "tooltip", style: {
3292
+ position: "fixed",
3293
+ opacity: 0,
3294
+ fontSize: "12px",
3295
+ padding: "4px 8px"
3296
+ }, children: renderContent() })] });
3297
+ };
3298
+
85
3299
  // dist/lib/components/icon-button.js
86
3300
  var IconButton = (props) => {
87
- const { className = "", style, color, icon, label, onClick, children } = props;
88
- const iconStyle = (0, import_hooks.useMemo)(() => {
3301
+ const { className = "", style, color, icon, label, tooltip, onClick, children } = props;
3302
+ const tooltipContent = tooltip === false ? void 0 : tooltip ?? label;
3303
+ const iconStyle = (0, import_hooks2.useMemo)(() => {
89
3304
  const css = getCSSMask(icon);
90
3305
  if (!color)
91
3306
  return css;
92
3307
  return { ...css, backgroundColor: color };
93
3308
  }, [color, icon]);
94
- return (0, import_jsx_runtime2.jsx)("div", { className: "deck-widget-button", style, children: (0, import_jsx_runtime2.jsx)("button", { className: `deck-widget-icon-button ${className}`, type: "button", onClick, title: label, children: children ? children : (0, import_jsx_runtime2.jsx)("div", { className: "deck-widget-icon", style: iconStyle }) }) });
3309
+ const button = (0, import_jsx_runtime3.jsx)("button", { className: `deck-widget-icon-button ${className}`, type: "button", onClick, "aria-label": label, children: children ? children : (0, import_jsx_runtime3.jsx)("div", { className: "deck-widget-icon", style: iconStyle }) });
3310
+ return (0, import_jsx_runtime3.jsx)("div", { className: "deck-widget-button", style, children: tooltipContent ? (0, import_jsx_runtime3.jsx)(Tooltip, { content: tooltipContent, children: button }) : button });
95
3311
  };
96
3312
 
97
3313
  // dist/zoom-widget.js
98
- var ZoomWidget = class extends import_core.Widget {
3314
+ var ZoomWidget = class extends Widget {
99
3315
  constructor(props = {}) {
100
3316
  super(props);
101
3317
  this.className = "deck-widget-zoom";
@@ -108,13 +3324,13 @@ var ZoomWidget = class extends import_core.Widget {
108
3324
  super.setProps(props);
109
3325
  }
110
3326
  onRenderHTML(rootElement) {
111
- 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" })] });
3327
+ const ui = (0, import_jsx_runtime4.jsxs)(ButtonGroup, { orientation: this.props.orientation, children: [(0, import_jsx_runtime4.jsx)(IconButton, { onClick: () => this.handleZoomIn(), label: this.props.zoomInLabel, tooltip: this.props.zoomInTooltip, className: "deck-widget-zoom-in" }), (0, import_jsx_runtime4.jsx)(IconButton, { onClick: () => this.handleZoomOut(), label: this.props.zoomOutLabel, tooltip: this.props.zoomOutTooltip, className: "deck-widget-zoom-out" })] });
112
3328
  (0, import_preact.render)(ui, rootElement);
113
3329
  }
114
3330
  isOrthographicView(viewId) {
115
3331
  const deck = this.deck;
116
3332
  const view = (deck == null ? void 0 : deck.isInitialized) && deck.getView(viewId);
117
- return view instanceof import_core.OrthographicView;
3333
+ return view instanceof OrthographicView;
118
3334
  }
119
3335
  handleZoom(viewId, delta) {
120
3336
  var _a, _b, _c, _d;
@@ -126,13 +3342,13 @@ var ZoomWidget = class extends import_core.Widget {
126
3342
  let nextZoom;
127
3343
  let nextZoomY;
128
3344
  if (zoomAxis === "X") {
129
- nextZoom = clamp(zoomX + delta, minZoomX, maxZoomX);
3345
+ nextZoom = clamp5(zoomX + delta, minZoomX, maxZoomX);
130
3346
  nextZoomY = zoomY;
131
3347
  } else if (zoomAxis === "Y") {
132
3348
  nextZoom = zoomX;
133
- nextZoomY = clamp(zoomY + delta, minZoomY, maxZoomY);
3349
+ nextZoomY = clamp5(zoomY + delta, minZoomY, maxZoomY);
134
3350
  } else {
135
- const clampedDelta = clamp(delta, Math.max(minZoomX - zoomX, minZoomY - zoomY), Math.min(maxZoomX - zoomX, maxZoomY - zoomY));
3351
+ const clampedDelta = clamp5(delta, Math.max(minZoomX - zoomX, minZoomY - zoomY), Math.min(maxZoomX - zoomX, maxZoomY - zoomY));
136
3352
  nextZoom = zoomX + clampedDelta;
137
3353
  nextZoomY = zoomY + clampedDelta;
138
3354
  }
@@ -149,7 +3365,7 @@ var ZoomWidget = class extends import_core.Widget {
149
3365
  });
150
3366
  } else {
151
3367
  const { zoom = 0, minZoom, maxZoom } = viewState;
152
- const nextZoom = clamp(zoom + delta, minZoom, maxZoom);
3368
+ const nextZoom = clamp5(zoom + delta, minZoom, maxZoom);
153
3369
  newViewState.zoom = nextZoom;
154
3370
  (_d = (_c = this.props).onZoom) == null ? void 0 : _d.call(_c, {
155
3371
  viewId,
@@ -163,7 +3379,7 @@ var ZoomWidget = class extends import_core.Widget {
163
3379
  };
164
3380
  if (this.props.transitionDuration > 0) {
165
3381
  nextViewState.transitionDuration = this.props.transitionDuration;
166
- nextViewState.transitionInterpolator = "latitude" in nextViewState ? new import_core.FlyToInterpolator() : new import_core.LinearInterpolator({
3382
+ nextViewState.transitionInterpolator = "latitude" in nextViewState ? new FlyToInterpolator() : new LinearInterpolator({
167
3383
  transitionProps: "zoomX" in newViewState ? ["zoomX", "zoomY"] : ["zoom"]
168
3384
  });
169
3385
  }
@@ -181,7 +3397,7 @@ var ZoomWidget = class extends import_core.Widget {
181
3397
  }
182
3398
  };
183
3399
  ZoomWidget.defaultProps = {
184
- ...import_core.Widget.defaultProps,
3400
+ ...Widget.defaultProps,
185
3401
  id: "zoom",
186
3402
  placement: "top-left",
187
3403
  orientation: "vertical",
@@ -189,12 +3405,14 @@ ZoomWidget.defaultProps = {
189
3405
  zoomStep: 1,
190
3406
  zoomInLabel: "Zoom In",
191
3407
  zoomOutLabel: "Zoom Out",
3408
+ zoomInTooltip: void 0,
3409
+ zoomOutTooltip: void 0,
192
3410
  zoomAxis: "all",
193
3411
  viewId: null,
194
3412
  onZoom: () => {
195
3413
  }
196
3414
  };
197
- function clamp(zoom, minZoom, maxZoom) {
3415
+ function clamp5(zoom, minZoom, maxZoom) {
198
3416
  return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
199
3417
  }
200
3418
  function normalizeOrthographicViewState({ zoom = 0, zoomX, zoomY, minZoom = -Infinity, maxZoom = Infinity, minZoomX = minZoom, maxZoomX = maxZoom, minZoomY = minZoom, maxZoomY = maxZoom }) {
@@ -204,10 +3422,9 @@ function normalizeOrthographicViewState({ zoom = 0, zoomX, zoomY, minZoom = -Inf
204
3422
  }
205
3423
 
206
3424
  // dist/reset-view-widget.js
207
- var import_jsx_runtime4 = require("preact/jsx-runtime");
3425
+ var import_jsx_runtime5 = require("preact/jsx-runtime");
208
3426
  var import_preact2 = require("preact");
209
- var import_core2 = require("@deck.gl/core");
210
- var ResetViewWidget = class extends import_core2.Widget {
3427
+ var ResetViewWidget = class extends Widget {
211
3428
  constructor(props = {}) {
212
3429
  super(props);
213
3430
  this.className = "deck-widget-reset-view";
@@ -220,7 +3437,7 @@ var ResetViewWidget = class extends import_core2.Widget {
220
3437
  super.setProps(props);
221
3438
  }
222
3439
  onRenderHTML(rootElement) {
223
- (0, import_preact2.render)((0, import_jsx_runtime4.jsx)(IconButton, { className: "deck-widget-reset-focus", label: this.props.label, onClick: this.handleClick.bind(this) }), rootElement);
3440
+ (0, import_preact2.render)((0, import_jsx_runtime5.jsx)(IconButton, { className: "deck-widget-reset-focus", label: this.props.label, tooltip: this.props.tooltip, onClick: this.handleClick.bind(this) }), rootElement);
224
3441
  }
225
3442
  handleClick() {
226
3443
  var _a;
@@ -242,10 +3459,11 @@ var ResetViewWidget = class extends import_core2.Widget {
242
3459
  }
243
3460
  };
244
3461
  ResetViewWidget.defaultProps = {
245
- ...import_core2.Widget.defaultProps,
3462
+ ...Widget.defaultProps,
246
3463
  id: "reset-view",
247
3464
  placement: "top-left",
248
3465
  label: "Reset View",
3466
+ tooltip: void 0,
249
3467
  initialViewState: void 0,
250
3468
  viewId: null,
251
3469
  onReset: () => {
@@ -253,10 +3471,9 @@ ResetViewWidget.defaultProps = {
253
3471
  };
254
3472
 
255
3473
  // dist/gimbal-widget.js
256
- var import_jsx_runtime5 = require("preact/jsx-runtime");
257
- var import_core3 = require("@deck.gl/core");
3474
+ var import_jsx_runtime6 = require("preact/jsx-runtime");
258
3475
  var import_preact3 = require("preact");
259
- var GimbalWidget = class extends import_core3.Widget {
3476
+ var GimbalWidget = class extends Widget {
260
3477
  constructor(props = {}) {
261
3478
  super(props);
262
3479
  this.className = "deck-widget-gimbal";
@@ -274,21 +3491,22 @@ var GimbalWidget = class extends import_core3.Widget {
274
3491
  const viewId = this.viewId || ((_a = Object.values(this.viewports)[0]) == null ? void 0 : _a.id) || "default-view";
275
3492
  const widgetViewport = this.viewports[viewId];
276
3493
  const { rotationOrbit, rotationX } = this.getNormalizedRotation(widgetViewport);
277
- const ui = (0, import_jsx_runtime5.jsx)("div", { className: "deck-widget-button", style: { perspective: 100, pointerEvents: "auto" }, children: (0, import_jsx_runtime5.jsxs)("button", { type: "button", onClick: () => {
3494
+ const tooltipContent = this.props.tooltip === false ? void 0 : this.props.tooltip ?? this.props.label;
3495
+ const ui = (0, import_jsx_runtime6.jsx)("div", { className: "deck-widget-button", style: { perspective: 100, pointerEvents: "auto" }, children: (0, import_jsx_runtime6.jsx)(Tooltip, { content: tooltipContent, children: (0, import_jsx_runtime6.jsxs)("button", { type: "button", onClick: () => {
278
3496
  for (const viewport of Object.values(this.viewports)) {
279
3497
  this.resetOrbitView(viewport);
280
3498
  }
281
- }, title: this.props.label, style: { position: "relative", width: 26, height: 26 }, children: [(0, import_jsx_runtime5.jsx)("svg", { className: "gimbal-outer-ring", width: "100%", height: "100%", viewBox: "0 0 26 26", style: {
3499
+ }, "aria-label": this.props.label, style: { position: "relative", width: 26, height: 26 }, children: [(0, import_jsx_runtime6.jsx)("svg", { className: "gimbal-outer-ring", width: "100%", height: "100%", viewBox: "0 0 26 26", style: {
282
3500
  position: "absolute",
283
3501
  top: 0,
284
3502
  left: 0,
285
3503
  transform: `rotateY(${rotationOrbit}deg)`
286
- }, children: (0, import_jsx_runtime5.jsx)("circle", { cx: "13", cy: "13", r: "10", stroke: "var(--icon-gimbal-outer-color, rgb(68, 92, 204))", strokeWidth: this.props.strokeWidth, fill: "none" }) }), (0, import_jsx_runtime5.jsx)("svg", { className: "gimbal-inner-ring", width: "100%", height: "100%", viewBox: "0 0 26 26", style: {
3504
+ }, children: (0, import_jsx_runtime6.jsx)("circle", { cx: "13", cy: "13", r: "10", stroke: "var(--icon-gimbal-outer-color, rgb(68, 92, 204))", strokeWidth: this.props.strokeWidth, fill: "none" }) }), (0, import_jsx_runtime6.jsx)("svg", { className: "gimbal-inner-ring", width: "100%", height: "100%", viewBox: "0 0 26 26", style: {
287
3505
  position: "absolute",
288
3506
  top: 0,
289
3507
  left: 0,
290
3508
  transform: `rotateX(${rotationX}deg)`
291
- }, children: (0, import_jsx_runtime5.jsx)("circle", { cx: "13", cy: "13", r: "7", stroke: "var(--icon-gimbal-inner-color, rgb(240, 92, 68))", strokeWidth: this.props.strokeWidth, fill: "none" }) })] }) });
3509
+ }, children: (0, import_jsx_runtime6.jsx)("circle", { cx: "13", cy: "13", r: "7", stroke: "var(--icon-gimbal-inner-color, rgb(240, 92, 68))", strokeWidth: this.props.strokeWidth, fill: "none" }) })] }) }) });
292
3510
  (0, import_preact3.render)(ui, rootElement);
293
3511
  }
294
3512
  onViewportChange(viewport) {
@@ -306,7 +3524,7 @@ var GimbalWidget = class extends import_core3.Widget {
306
3524
  rotationOrbit: 0,
307
3525
  rotationX: 0,
308
3526
  transitionDuration: this.props.transitionDuration,
309
- transitionInterpolator: new import_core3.LinearInterpolator({
3527
+ transitionInterpolator: new LinearInterpolator({
310
3528
  transitionProps: ["rotationOrbit", "rotationX"]
311
3529
  })
312
3530
  };
@@ -329,11 +3547,12 @@ var GimbalWidget = class extends import_core3.Widget {
329
3547
  }
330
3548
  };
331
3549
  GimbalWidget.defaultProps = {
332
- ...import_core3.Widget.defaultProps,
3550
+ ...Widget.defaultProps,
333
3551
  id: "gimbal",
334
3552
  placement: "top-left",
335
3553
  viewId: null,
336
3554
  label: "Gimbal",
3555
+ tooltip: void 0,
337
3556
  strokeWidth: 1.5,
338
3557
  transitionDuration: 200,
339
3558
  onReset: () => {
@@ -354,10 +3573,9 @@ function normalizeAndClampAngle(angle) {
354
3573
  }
355
3574
 
356
3575
  // dist/compass-widget.js
357
- var import_jsx_runtime6 = require("preact/jsx-runtime");
358
- var import_core4 = require("@deck.gl/core");
3576
+ var import_jsx_runtime7 = require("preact/jsx-runtime");
359
3577
  var import_preact4 = require("preact");
360
- var CompassWidget = class extends import_core4.Widget {
3578
+ var CompassWidget = class extends Widget {
361
3579
  constructor(props = {}) {
362
3580
  super(props);
363
3581
  this.className = "deck-widget-compass";
@@ -375,11 +3593,12 @@ var CompassWidget = class extends import_core4.Widget {
375
3593
  const viewId = this.viewId || ((_a = Object.values(this.viewports)[0]) == null ? void 0 : _a.id);
376
3594
  const widgetViewport = this.viewports[viewId];
377
3595
  const [rz, rx] = this.getRotation(widgetViewport);
378
- const ui = (0, import_jsx_runtime6.jsx)("div", { className: "deck-widget-button", style: { perspective: 100 }, children: (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: () => {
3596
+ const tooltipContent = this.props.tooltip === false ? void 0 : this.props.tooltip ?? this.props.label;
3597
+ const ui = (0, import_jsx_runtime7.jsx)("div", { className: "deck-widget-button", style: { perspective: 100 }, children: (0, import_jsx_runtime7.jsx)(Tooltip, { content: tooltipContent, children: (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: () => {
379
3598
  for (const viewport of Object.values(this.viewports)) {
380
3599
  this.handleCompassReset(viewport);
381
3600
  }
382
- }, title: this.props.label, style: { transform: `rotateX(${rx}deg)` }, children: (0, import_jsx_runtime6.jsx)("svg", { fill: "none", width: "100%", height: "100%", viewBox: "0 0 26 26", children: (0, import_jsx_runtime6.jsxs)("g", { transform: `rotate(${rz},13,13)`, children: [(0, import_jsx_runtime6.jsx)("path", { d: "M10 13.0001L12.9999 5L15.9997 13.0001H10Z", fill: "var(--icon-compass-north-color, rgb(240, 92, 68))" }), (0, import_jsx_runtime6.jsx)("path", { d: "M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z", fill: "var(--icon-compass-south-color, rgb(204, 204, 204))" })] }) }) }) });
3601
+ }, "aria-label": this.props.label, style: { transform: `rotateX(${rx}deg)` }, children: (0, import_jsx_runtime7.jsx)("svg", { fill: "none", width: "100%", height: "100%", viewBox: "0 0 26 26", children: (0, import_jsx_runtime7.jsxs)("g", { transform: `rotate(${rz},13,13)`, children: [(0, import_jsx_runtime7.jsx)("path", { d: "M10 13.0001L12.9999 5L15.9997 13.0001H10Z", fill: "var(--icon-compass-north-color, rgb(240, 92, 68))" }), (0, import_jsx_runtime7.jsx)("path", { d: "M16.0002 12.9999L13.0004 21L10.0005 12.9999H16.0002Z", fill: "var(--icon-compass-south-color, rgb(204, 204, 204))" })] }) }) }) }) });
383
3602
  (0, import_preact4.render)(ui, rootElement);
384
3603
  }
385
3604
  onViewportChange(viewport) {
@@ -389,9 +3608,9 @@ var CompassWidget = class extends import_core4.Widget {
389
3608
  }
390
3609
  }
391
3610
  getRotation(viewport) {
392
- if (viewport instanceof import_core4.WebMercatorViewport) {
3611
+ if (viewport instanceof WebMercatorViewport) {
393
3612
  return [-viewport.bearing, viewport.pitch];
394
- } else if (viewport instanceof import_core4._GlobeViewport) {
3613
+ } else if (viewport instanceof GlobeViewport) {
395
3614
  return [0, Math.max(-80, Math.min(80, viewport.latitude))];
396
3615
  }
397
3616
  return [0, 0];
@@ -399,7 +3618,7 @@ var CompassWidget = class extends import_core4.Widget {
399
3618
  handleCompassReset(viewport) {
400
3619
  var _a, _b;
401
3620
  const viewId = this.viewId || viewport.id;
402
- if (viewport instanceof import_core4.WebMercatorViewport) {
3621
+ if (viewport instanceof WebMercatorViewport) {
403
3622
  const viewState = this.getViewState(viewId);
404
3623
  const resetPitch = this.getRotation(viewport)[0] === 0;
405
3624
  const nextBearing = 0;
@@ -410,28 +3629,28 @@ var CompassWidget = class extends import_core4.Widget {
410
3629
  bearing: nextBearing,
411
3630
  ...resetPitch ? { pitch: nextPitch } : {},
412
3631
  transitionDuration: this.props.transitionDuration,
413
- transitionInterpolator: new import_core4.FlyToInterpolator()
3632
+ transitionInterpolator: new FlyToInterpolator()
414
3633
  };
415
3634
  this.setViewState(viewId, nextViewState);
416
3635
  }
417
3636
  }
418
3637
  };
419
3638
  CompassWidget.defaultProps = {
420
- ...import_core4.Widget.defaultProps,
3639
+ ...Widget.defaultProps,
421
3640
  id: "compass",
422
3641
  placement: "top-left",
423
3642
  viewId: null,
424
3643
  label: "Reset Compass",
3644
+ tooltip: void 0,
425
3645
  transitionDuration: 200,
426
3646
  onReset: () => {
427
3647
  }
428
3648
  };
429
3649
 
430
3650
  // dist/scale-widget.js
431
- var import_jsx_runtime7 = require("preact/jsx-runtime");
3651
+ var import_jsx_runtime8 = require("preact/jsx-runtime");
432
3652
  var import_preact5 = require("preact");
433
- var import_core5 = require("@deck.gl/core");
434
- var ScaleWidget = class extends import_core5.Widget {
3653
+ var ScaleWidget = class extends Widget {
435
3654
  constructor(props = {}) {
436
3655
  super(props);
437
3656
  this.className = "deck-widget-scale";
@@ -450,7 +3669,7 @@ var ScaleWidget = class extends import_core5.Widget {
450
3669
  const lineOffsetX = 50;
451
3670
  const svgWidth = lineOffsetX + this.scaleWidth;
452
3671
  const tickHeight = 10;
453
- (0, import_preact5.render)((0, import_jsx_runtime7.jsxs)("svg", { className: "deck-widget-scale", width: svgWidth, height: 30, style: { overflow: "visible", background: "transparent" }, onClick: this.handleClick.bind(this), children: [(0, import_jsx_runtime7.jsx)("text", { x: lineOffsetX + 5, y: "10", textAnchor: "end", alignmentBaseline: "middle", style: { fontSize: "16px", fill: "black", fontWeight: "bold", fontFamily: "sans-serif" }, children: this.scaleText }), (0, import_jsx_runtime7.jsx)("line", { x1: lineOffsetX, y1: "15", x2: lineOffsetX + this.scaleWidth, y2: "15", stroke: "black", strokeWidth: "6" }), (0, import_jsx_runtime7.jsx)("line", { x1: lineOffsetX, y1: "15", x2: lineOffsetX, y2: 15 - tickHeight, stroke: "black", strokeWidth: "6" }), (0, import_jsx_runtime7.jsx)("line", { x1: lineOffsetX + this.scaleWidth, y1: "15", x2: lineOffsetX + this.scaleWidth, y2: 15 - tickHeight, stroke: "black", strokeWidth: "6" })] }), rootElement);
3672
+ (0, import_preact5.render)((0, import_jsx_runtime8.jsxs)("svg", { className: "deck-widget-scale", width: svgWidth, height: 30, style: { overflow: "visible", background: "transparent" }, onClick: this.handleClick.bind(this), children: [(0, import_jsx_runtime8.jsx)("text", { x: lineOffsetX + 5, y: "10", textAnchor: "end", alignmentBaseline: "middle", style: { fontSize: "16px", fill: "black", fontWeight: "bold", fontFamily: "sans-serif" }, children: this.scaleText }), (0, import_jsx_runtime8.jsx)("line", { x1: lineOffsetX, y1: "15", x2: lineOffsetX + this.scaleWidth, y2: "15", stroke: "black", strokeWidth: "6" }), (0, import_jsx_runtime8.jsx)("line", { x1: lineOffsetX, y1: "15", x2: lineOffsetX, y2: 15 - tickHeight, stroke: "black", strokeWidth: "6" }), (0, import_jsx_runtime8.jsx)("line", { x1: lineOffsetX + this.scaleWidth, y1: "15", x2: lineOffsetX + this.scaleWidth, y2: 15 - tickHeight, stroke: "black", strokeWidth: "6" })] }), rootElement);
454
3673
  }
455
3674
  onViewportChange(viewport) {
456
3675
  if (!("latitude" in viewport))
@@ -471,7 +3690,7 @@ var ScaleWidget = class extends import_core5.Widget {
471
3690
  }
472
3691
  };
473
3692
  ScaleWidget.defaultProps = {
474
- ...import_core5.Widget.defaultProps,
3693
+ ...Widget.defaultProps,
475
3694
  id: "scale",
476
3695
  placement: "bottom-left",
477
3696
  label: "Scale",
@@ -513,14 +3732,12 @@ function computeScaleCandidate(metersPerPixel) {
513
3732
  }
514
3733
 
515
3734
  // dist/geocoder-widget.js
516
- var import_jsx_runtime9 = require("preact/jsx-runtime");
517
- var import_core6 = require("@deck.gl/core");
518
- var import_core7 = require("@deck.gl/core");
3735
+ var import_jsx_runtime10 = require("preact/jsx-runtime");
519
3736
  var import_preact6 = require("preact");
520
3737
 
521
3738
  // dist/lib/components/dropdown-menu.js
522
- var import_jsx_runtime8 = require("preact/jsx-runtime");
523
- var import_hooks2 = require("preact/hooks");
3739
+ var import_jsx_runtime9 = require("preact/jsx-runtime");
3740
+ var import_hooks3 = require("preact/hooks");
524
3741
  function getMenuItemValue(item) {
525
3742
  return typeof item === "string" ? item : item.value;
526
3743
  }
@@ -531,12 +3748,12 @@ function getMenuItemIcon(item) {
531
3748
  return typeof item === "string" ? void 0 : item.icon;
532
3749
  }
533
3750
  var DropdownMenu = (props) => {
534
- const [isOpen, setIsOpen] = (0, import_hooks2.useState)(false);
535
- return (0, import_jsx_runtime8.jsx)(SimpleMenu, { ...props, style: { ...props.style, position: "absolute" }, isOpen, onClose: () => setIsOpen(false), trigger: (0, import_jsx_runtime8.jsx)("button", { className: "deck-widget-dropdown-button", onClick: () => setIsOpen(!isOpen), children: (0, import_jsx_runtime8.jsx)("span", { className: `deck-widget-dropdown-icon ${isOpen ? "open" : ""}` }) }) });
3751
+ const [isOpen, setIsOpen] = (0, import_hooks3.useState)(false);
3752
+ return (0, import_jsx_runtime9.jsx)(SimpleMenu, { ...props, style: { ...props.style, position: "absolute" }, isOpen, onClose: () => setIsOpen(false), trigger: (0, import_jsx_runtime9.jsx)("button", { className: "deck-widget-dropdown-button", onClick: () => setIsOpen(!isOpen), children: (0, import_jsx_runtime9.jsx)("span", { className: `deck-widget-dropdown-icon ${isOpen ? "open" : ""}` }) }) });
536
3753
  };
537
3754
  var SimpleMenu = (props) => {
538
- const dropdownRef = (0, import_hooks2.useRef)(null);
539
- (0, import_hooks2.useEffect)(() => {
3755
+ const dropdownRef = (0, import_hooks3.useRef)(null);
3756
+ (0, import_hooks3.useEffect)(() => {
540
3757
  const handleClickOutside = (event) => {
541
3758
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
542
3759
  props.onClose();
@@ -560,10 +3777,10 @@ var SimpleMenu = (props) => {
560
3777
  if (props.menuItems.length === 0) {
561
3778
  return null;
562
3779
  }
563
- return (0, import_jsx_runtime8.jsxs)("div", { className: "deck-widget-dropdown-container", ref: dropdownRef, children: [props.trigger, props.isOpen && (0, import_jsx_runtime8.jsx)("ul", { className: "deck-widget-dropdown-menu", style: props.style, children: props.menuItems.map((item, i) => {
3780
+ return (0, import_jsx_runtime9.jsxs)("div", { className: "deck-widget-dropdown-container", ref: dropdownRef, children: [props.trigger, props.isOpen && (0, import_jsx_runtime9.jsx)("ul", { className: "deck-widget-dropdown-menu", style: props.style, children: props.menuItems.map((item, i) => {
564
3781
  const value = getMenuItemValue(item);
565
3782
  const icon = getMenuItemIcon(item);
566
- return (0, import_jsx_runtime8.jsxs)("li", { className: `deck-widget-dropdown-item ${value ? "" : "disabled"}`, onClick: () => handleSelect(value, item), children: [icon && (0, import_jsx_runtime8.jsx)("span", { className: "deck-widget-dropdown-item-icon", style: getCSSMask(icon) }), getMenuItemLabel(item)] }, i);
3783
+ return (0, import_jsx_runtime9.jsxs)("li", { className: `deck-widget-dropdown-item ${value ? "" : "disabled"}`, onClick: () => handleSelect(value, item), children: [icon && (0, import_jsx_runtime9.jsx)("span", { className: "deck-widget-dropdown-item-icon", style: getCSSMask(icon) }), getMenuItemLabel(item)] }, i);
567
3784
  }) })] });
568
3785
  };
569
3786
 
@@ -770,7 +3987,7 @@ var CURRENT_LOCATION_ITEM = {
770
3987
  value: CURRENT_LOCATION2,
771
3988
  icon: LOCATION_ICON
772
3989
  };
773
- var GeocoderWidget = class extends import_core6.Widget {
3990
+ var GeocoderWidget = class extends Widget {
774
3991
  constructor(props = {}) {
775
3992
  super(props);
776
3993
  this.className = "deck-widget-geocoder";
@@ -839,7 +4056,7 @@ var GeocoderWidget = class extends import_core6.Widget {
839
4056
  }
840
4057
  onRenderHTML(rootElement) {
841
4058
  const menuItems = this.props._geolocation ? [CURRENT_LOCATION_ITEM, ...this.geocodeHistory.addressHistory] : [...this.geocodeHistory.addressHistory];
842
- (0, import_preact6.render)((0, import_jsx_runtime9.jsxs)("div", { className: "deck-widget-geocoder", children: [(0, import_jsx_runtime9.jsx)("input", {
4059
+ (0, import_preact6.render)((0, import_jsx_runtime10.jsxs)("div", { className: "deck-widget-geocoder", children: [(0, import_jsx_runtime10.jsx)("input", {
843
4060
  className: "deck-widget-geocoder-input",
844
4061
  type: "text",
845
4062
  placeholder: this.isGettingLocation ? "Finding your location..." : this.geocoder.placeholderLocation ?? "Enter address or location",
@@ -850,7 +4067,7 @@ var GeocoderWidget = class extends import_core6.Widget {
850
4067
  return this.setInput(((_a = e.target) == null ? void 0 : _a.value) || "");
851
4068
  },
852
4069
  onKeyPress: this.handleKeyPress
853
- }), (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);
4070
+ }), (0, import_jsx_runtime10.jsx)(DropdownMenu, { menuItems, onSelect: this.handleSelect }), this.geocodeHistory.errorText && (0, import_jsx_runtime10.jsx)("div", { className: "deck-widget-geocoder-error", children: this.geocodeHistory.errorText })] }), rootElement);
854
4071
  }
855
4072
  flyTo(viewState) {
856
4073
  var _a, _b;
@@ -872,14 +4089,14 @@ var GeocoderWidget = class extends import_core6.Widget {
872
4089
  };
873
4090
  if (this.props.transitionDuration > 0) {
874
4091
  nextViewState.transitionDuration = this.props.transitionDuration;
875
- nextViewState.transitionInterpolator = "latitude" in nextViewState ? new import_core7.FlyToInterpolator() : new import_core7.LinearInterpolator();
4092
+ nextViewState.transitionInterpolator = "latitude" in nextViewState ? new FlyToInterpolator() : new LinearInterpolator();
876
4093
  }
877
4094
  this.setViewState(viewId, nextViewState);
878
4095
  }
879
4096
  }
880
4097
  };
881
4098
  GeocoderWidget.defaultProps = {
882
- ...import_core6.Widget.defaultProps,
4099
+ ...Widget.defaultProps,
883
4100
  id: "geocoder",
884
4101
  viewId: null,
885
4102
  placement: "top-left",
@@ -913,10 +4130,9 @@ function getGeocoder(props) {
913
4130
  }
914
4131
 
915
4132
  // dist/fullscreen-widget.js
916
- var import_jsx_runtime10 = require("preact/jsx-runtime");
917
- var import_core8 = require("@deck.gl/core");
4133
+ var import_jsx_runtime11 = require("preact/jsx-runtime");
918
4134
  var import_preact7 = require("preact");
919
- var FullscreenWidget = class extends import_core8.Widget {
4135
+ var FullscreenWidget = class extends Widget {
920
4136
  constructor(props = {}) {
921
4137
  super(props);
922
4138
  this.className = "deck-widget-fullscreen";
@@ -932,9 +4148,9 @@ var FullscreenWidget = class extends import_core8.Widget {
932
4148
  }
933
4149
  onRenderHTML(rootElement) {
934
4150
  const isFullscreen = this.getFullscreen();
935
- (0, import_preact7.render)((0, import_jsx_runtime10.jsx)(IconButton, { onClick: () => {
936
- this.handleClick().catch((err) => import_core8.log.error(err)());
937
- }, label: isFullscreen ? this.props.exitLabel : this.props.enterLabel, className: isFullscreen ? "deck-widget-fullscreen-exit" : "deck-widget-fullscreen-enter" }), rootElement);
4151
+ (0, import_preact7.render)((0, import_jsx_runtime11.jsx)(IconButton, { onClick: () => {
4152
+ this.handleClick().catch((err) => log_default.error(err)());
4153
+ }, label: isFullscreen ? this.props.exitLabel : this.props.enterLabel, tooltip: isFullscreen ? this.props.exitTooltip : this.props.enterTooltip, className: isFullscreen ? "deck-widget-fullscreen-exit" : "deck-widget-fullscreen-enter" }), rootElement);
938
4154
  }
939
4155
  setProps(props) {
940
4156
  this.placement = props.placement ?? this.placement;
@@ -989,29 +4205,29 @@ var FullscreenWidget = class extends import_core8.Widget {
989
4205
  }
990
4206
  };
991
4207
  FullscreenWidget.defaultProps = {
992
- ...import_core8.Widget.defaultProps,
4208
+ ...Widget.defaultProps,
993
4209
  id: "fullscreen",
994
4210
  placement: "top-left",
995
4211
  viewId: null,
996
4212
  enterLabel: "Enter Fullscreen",
997
4213
  exitLabel: "Exit Fullscreen",
4214
+ enterTooltip: void 0,
4215
+ exitTooltip: void 0,
998
4216
  container: void 0,
999
4217
  onFullscreenChange: () => {
1000
4218
  }
1001
4219
  };
1002
4220
 
1003
4221
  // dist/splitter-widget.js
1004
- var import_jsx_runtime11 = require("preact/jsx-runtime");
4222
+ var import_jsx_runtime12 = require("preact/jsx-runtime");
1005
4223
  var import_preact8 = require("preact");
1006
- var import_hooks3 = require("preact/hooks");
1007
- var import_core10 = require("@deck.gl/core");
4224
+ var import_hooks4 = require("preact/hooks");
1008
4225
 
1009
4226
  // dist/view-layout/view-layout.js
1010
- var import_core9 = require("@deck.gl/core");
1011
4227
  var VIEW_LAYOUT_TYPES = /* @__PURE__ */ new Set(["row", "column", "overlay", "spacer"]);
1012
4228
  var VIEW_LAYOUT_ORIENTATIONS = /* @__PURE__ */ new Set(["horizontal", "vertical"]);
1013
4229
  function isViewLayout(child) {
1014
- return Boolean(child && typeof child === "object" && !(child instanceof import_core9.View) && ("type" in child && VIEW_LAYOUT_TYPES.has(String(child.type)) || "orientation" in child && VIEW_LAYOUT_ORIENTATIONS.has(String(child.orientation))));
4230
+ return Boolean(child && typeof child === "object" && !(child instanceof View) && ("type" in child && VIEW_LAYOUT_TYPES.has(String(child.type)) || "orientation" in child && VIEW_LAYOUT_ORIENTATIONS.has(String(child.orientation))));
1015
4231
  }
1016
4232
  function getViewLayoutType(item) {
1017
4233
  if ("orientation" in item) {
@@ -1061,7 +4277,7 @@ function assertViewLayoutChild(child) {
1061
4277
  assertViewLayout(child);
1062
4278
  return;
1063
4279
  }
1064
- if (child instanceof import_core9.View) {
4280
+ if (child instanceof View) {
1065
4281
  return;
1066
4282
  }
1067
4283
  throw new Error("ViewLayout children must be deck.gl View instances or layout objects.");
@@ -1563,7 +4779,7 @@ function evaluateViews(layout, viewLayouts, splitValues) {
1563
4779
  height: `${view.props.height}%`
1564
4780
  }));
1565
4781
  }
1566
- var SplitterWidget = class extends import_core10.Widget {
4782
+ var SplitterWidget = class extends Widget {
1567
4783
  constructor(props) {
1568
4784
  super(props);
1569
4785
  this.className = "deck-widget-splitter";
@@ -1573,7 +4789,7 @@ var SplitterWidget = class extends import_core10.Widget {
1573
4789
  this.updateViewLayout(this.props.viewLayout);
1574
4790
  }
1575
4791
  setProps(props) {
1576
- if (props.viewLayout && !(0, import_core10._deepEqual)(props.viewLayout, this.props.viewLayout, -1)) {
4792
+ if (props.viewLayout && !deepEqual(props.viewLayout, this.props.viewLayout, -1)) {
1577
4793
  this.updateViewLayout(props.viewLayout);
1578
4794
  this.views = void 0;
1579
4795
  }
@@ -1621,11 +4837,11 @@ var SplitterWidget = class extends import_core10.Widget {
1621
4837
  this.doUpdate();
1622
4838
  }
1623
4839
  onRenderHTML(rootElement) {
1624
- (0, import_preact8.render)((0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: this.viewLayouts.map((layout) => layout.editable && (0, import_jsx_runtime11.jsx)(Splitter, { ...layout, onChange: (newSplit) => this.onChange(newSplit, layout), onDragStart: () => this.props.onDragStart(), onDragEnd: () => this.props.onDragStart() })) }), rootElement);
4840
+ (0, import_preact8.render)((0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: this.viewLayouts.map((layout) => layout.editable && (0, import_jsx_runtime12.jsx)(Splitter, { ...layout, onChange: (newSplit) => this.onChange(newSplit, layout), onDragStart: () => this.props.onDragStart(), onDragEnd: () => this.props.onDragStart() })) }), rootElement);
1625
4841
  }
1626
4842
  };
1627
4843
  SplitterWidget.defaultProps = {
1628
- ...import_core10.Widget.defaultProps,
4844
+ ...Widget.defaultProps,
1629
4845
  id: "splitter-widget",
1630
4846
  viewLayout: void 0,
1631
4847
  onChange: () => {
@@ -1636,9 +4852,9 @@ SplitterWidget.defaultProps = {
1636
4852
  }
1637
4853
  };
1638
4854
  function Splitter({ orientation, x, y, width, height, split, minSplit, maxSplit, onChange, onDragStart, onDragEnd }) {
1639
- const [dragging, setDragging] = (0, import_hooks3.useState)(false);
1640
- const containerRef = (0, import_hooks3.useRef)(null);
1641
- (0, import_hooks3.useEffect)(() => {
4855
+ const [dragging, setDragging] = (0, import_hooks4.useState)(false);
4856
+ const containerRef = (0, import_hooks4.useRef)(null);
4857
+ (0, import_hooks4.useEffect)(() => {
1642
4858
  if (!dragging) {
1643
4859
  return void 0;
1644
4860
  }
@@ -1681,33 +4897,32 @@ function Splitter({ orientation, x, y, width, height, split, minSplit, maxSplit,
1681
4897
  width: `${width}%`,
1682
4898
  height: `${height}%`
1683
4899
  };
1684
- return (0, import_jsx_runtime11.jsx)("div", { ref: containerRef, style: containerStyle, children: (0, import_jsx_runtime11.jsx)("div", { className: `deck-widget-splitter-handle deck-widget-splitter-handle--${orientation} ${dragging ? "active" : ""}`, style: splitterStyle, onPointerDown: handleDragStart }) });
4900
+ return (0, import_jsx_runtime12.jsx)("div", { ref: containerRef, style: containerStyle, children: (0, import_jsx_runtime12.jsx)("div", { className: `deck-widget-splitter-handle deck-widget-splitter-handle--${orientation} ${dragging ? "active" : ""}`, style: splitterStyle, onPointerDown: handleDragStart }) });
1685
4901
  }
1686
4902
 
1687
4903
  // dist/info-widget.js
1688
- var import_jsx_runtime14 = require("preact/jsx-runtime");
1689
- var import_core11 = require("@deck.gl/core");
4904
+ var import_jsx_runtime15 = require("preact/jsx-runtime");
1690
4905
  var import_preact9 = require("preact");
1691
4906
 
1692
4907
  // dist/lib/components/popover.js
1693
- var import_jsx_runtime12 = require("preact/jsx-runtime");
1694
- var import_hooks4 = require("preact/hooks");
1695
- var import_dom = require("@floating-ui/dom");
4908
+ var import_jsx_runtime13 = require("preact/jsx-runtime");
4909
+ var import_hooks5 = require("preact/hooks");
4910
+ var import_dom2 = require("@floating-ui/dom");
1696
4911
  var Popover = ({ x, y, placement = "right", offset: pixelOffset = 0, arrow: arrowSize = false, arrowColor = "white", children }) => {
1697
- const anchorRef = (0, import_hooks4.useRef)(null);
1698
- const contentRef = (0, import_hooks4.useRef)(null);
1699
- const arrowRef = (0, import_hooks4.useRef)(null);
1700
- const updaterRef = (0, import_hooks4.useRef)();
4912
+ const anchorRef = (0, import_hooks5.useRef)(null);
4913
+ const contentRef = (0, import_hooks5.useRef)(null);
4914
+ const arrowRef = (0, import_hooks5.useRef)(null);
4915
+ const updaterRef = (0, import_hooks5.useRef)();
1701
4916
  updaterRef.current = () => {
1702
4917
  if (!anchorRef.current || !contentRef.current)
1703
4918
  return;
1704
4919
  const arrowWidth = Array.isArray(arrowSize) ? arrowSize[0] : arrowSize || 0;
1705
4920
  const arrowHeight = Array.isArray(arrowSize) ? arrowSize[1] : arrowSize || 0;
1706
4921
  const padding = pixelOffset + Math.max(arrowHeight, arrowWidth);
1707
- const middleware = placement.includes("-") ? [(0, import_dom.offset)(padding), (0, import_dom.flip)(), (0, import_dom.shift)()] : [(0, import_dom.offset)(padding), (0, import_dom.shift)(), (0, import_dom.flip)()];
4922
+ const middleware = placement.includes("-") ? [(0, import_dom2.offset)(padding), (0, import_dom2.flip)(), (0, import_dom2.shift)()] : [(0, import_dom2.offset)(padding), (0, import_dom2.shift)(), (0, import_dom2.flip)()];
1708
4923
  if (arrowRef.current)
1709
- middleware.push((0, import_dom.arrow)({ element: arrowRef.current }));
1710
- (0, import_dom.computePosition)(anchorRef.current, contentRef.current, {
4924
+ middleware.push((0, import_dom2.arrow)({ element: arrowRef.current }));
4925
+ (0, import_dom2.computePosition)(anchorRef.current, contentRef.current, {
1711
4926
  placement,
1712
4927
  strategy: "fixed",
1713
4928
  middleware
@@ -1726,18 +4941,18 @@ var Popover = ({ x, y, placement = "right", offset: pixelOffset = 0, arrow: arro
1726
4941
  }
1727
4942
  });
1728
4943
  };
1729
- (0, import_hooks4.useMemo)(() => {
4944
+ (0, import_hooks5.useMemo)(() => {
1730
4945
  var _a;
1731
4946
  (_a = updaterRef.current) == null ? void 0 : _a.call(updaterRef);
1732
4947
  }, [x, y, placement, arrowSize, pixelOffset]);
1733
- (0, import_hooks4.useEffect)(() => {
4948
+ (0, import_hooks5.useEffect)(() => {
1734
4949
  const anchor = anchorRef.current;
1735
4950
  const content = contentRef.current;
1736
4951
  if (!anchor || !content) {
1737
4952
  return void 0;
1738
4953
  }
1739
4954
  content.style.visibility = "visible";
1740
- const cleanup = (0, import_dom.autoUpdate)(anchor, content, () => {
4955
+ const cleanup = (0, import_dom2.autoUpdate)(anchor, content, () => {
1741
4956
  var _a;
1742
4957
  return (_a = updaterRef.current) == null ? void 0 : _a.call(updaterRef);
1743
4958
  });
@@ -1745,7 +4960,7 @@ var Popover = ({ x, y, placement = "right", offset: pixelOffset = 0, arrow: arro
1745
4960
  cleanup();
1746
4961
  };
1747
4962
  }, []);
1748
- return (0, import_jsx_runtime12.jsx)("div", { style: { position: "absolute", left: x, top: y }, ref: anchorRef, children: (0, import_jsx_runtime12.jsxs)("div", { className: "deck-widget deck-widget-popover", style: { position: "fixed", visibility: "hidden", pointerEvents: "none" }, ref: contentRef, children: [Boolean(arrowSize) && (0, import_jsx_runtime12.jsx)("div", { className: "deck-widget-popover-arrow", style: { position: "absolute" }, ref: arrowRef }), children] }) });
4963
+ return (0, import_jsx_runtime13.jsx)("div", { style: { position: "absolute", left: x, top: y }, ref: anchorRef, children: (0, import_jsx_runtime13.jsxs)("div", { className: "deck-widget deck-widget-popover", style: { position: "fixed", visibility: "hidden", pointerEvents: "none" }, ref: contentRef, children: [Boolean(arrowSize) && (0, import_jsx_runtime13.jsx)("div", { className: "deck-widget-popover-arrow", style: { position: "absolute" }, ref: arrowRef }), children] }) });
1749
4964
  };
1750
4965
  function createArrow(width, height, color, placement) {
1751
4966
  const result = {
@@ -1785,11 +5000,11 @@ function createArrow(width, height, color, placement) {
1785
5000
  }
1786
5001
 
1787
5002
  // dist/lib/components/user-content.js
1788
- var import_jsx_runtime13 = require("preact/jsx-runtime");
1789
- var import_hooks5 = require("preact/hooks");
5003
+ var import_jsx_runtime14 = require("preact/jsx-runtime");
5004
+ var import_hooks6 = require("preact/hooks");
1790
5005
  var UserContent = ({ text, html, element, ...props }) => {
1791
- const containerRef = (0, import_hooks5.useRef)(null);
1792
- (0, import_hooks5.useEffect)(() => {
5006
+ const containerRef = (0, import_hooks6.useRef)(null);
5007
+ (0, import_hooks6.useEffect)(() => {
1793
5008
  if (containerRef.current && element) {
1794
5009
  containerRef.current.append(element);
1795
5010
  }
@@ -1797,11 +5012,11 @@ var UserContent = ({ text, html, element, ...props }) => {
1797
5012
  element == null ? void 0 : element.remove();
1798
5013
  };
1799
5014
  }, [element]);
1800
- return (0, import_jsx_runtime13.jsx)("div", { ref: containerRef, ...props, dangerouslySetInnerHTML: html ? { __html: html } : void 0, children: text });
5015
+ return (0, import_jsx_runtime14.jsx)("div", { ref: containerRef, ...props, dangerouslySetInnerHTML: html ? { __html: html } : void 0, children: text });
1801
5016
  };
1802
5017
 
1803
5018
  // dist/info-widget.js
1804
- var InfoWidget = class extends import_core11.Widget {
5019
+ var InfoWidget = class extends Widget {
1805
5020
  constructor(props) {
1806
5021
  super(props);
1807
5022
  this.className = "deck-widget-info";
@@ -1867,12 +5082,12 @@ var InfoWidget = class extends import_core11.Widget {
1867
5082
  ...this.tooltip.style
1868
5083
  };
1869
5084
  const [x, y] = this.viewport.project(this.tooltip.position);
1870
- const ui = (0, import_jsx_runtime14.jsx)(Popover, { x, y, placement: this.props.placement, arrow: this.props.arrow, arrowColor: "var(--menu-background, #fff)", offset: this.props.offset, children: (0, import_jsx_runtime14.jsx)(UserContent, { className: `deck-widget-popup-content ${this.tooltip.className} ${this.props.className}`, style, html: this.tooltip.html, text: this.tooltip.text, element: this.tooltip.element }) });
5085
+ const ui = (0, import_jsx_runtime15.jsx)(Popover, { x, y, placement: this.props.placement, arrow: this.props.arrow, arrowColor: "var(--menu-background, #fff)", offset: this.props.offset, children: (0, import_jsx_runtime15.jsx)(UserContent, { className: `deck-widget-popup-content ${this.tooltip.className} ${this.props.className}`, style, html: this.tooltip.html, text: this.tooltip.text, element: this.tooltip.element }) });
1871
5086
  (0, import_preact9.render)(ui, rootElement);
1872
5087
  }
1873
5088
  };
1874
5089
  InfoWidget.defaultProps = {
1875
- ...import_core11.Widget.defaultProps,
5090
+ ...Widget.defaultProps,
1876
5091
  id: "info",
1877
5092
  viewId: null,
1878
5093
  mode: "hover",
@@ -1883,10 +5098,9 @@ InfoWidget.defaultProps = {
1883
5098
  };
1884
5099
 
1885
5100
  // dist/popup-widget.js
1886
- var import_jsx_runtime15 = require("preact/jsx-runtime");
1887
- var import_core12 = require("@deck.gl/core");
5101
+ var import_jsx_runtime16 = require("preact/jsx-runtime");
1888
5102
  var import_preact10 = require("preact");
1889
- var PopupWidget = class extends import_core12.Widget {
5103
+ var PopupWidget = class extends Widget {
1890
5104
  constructor(props) {
1891
5105
  super(props);
1892
5106
  this.className = "deck-widget-popup";
@@ -1930,12 +5144,12 @@ var PopupWidget = class extends import_core12.Widget {
1930
5144
  }
1931
5145
  const { marker, content, style } = this.props;
1932
5146
  const [x, y] = this.viewport.project(this.props.position);
1933
- const ui = (0, import_jsx_runtime15.jsxs)("div", { children: [marker && (0, import_jsx_runtime15.jsx)("div", { className: "deck-widget-popup-marker", style: { left: x, top: y }, children: (0, import_jsx_runtime15.jsx)(UserContent, { ...marker, onClick: () => this._setIsOpen(true) }) }), this.isOpen && (0, import_jsx_runtime15.jsx)(Popover, { x, y, placement: this.props.placement, arrow: this.props.arrow, arrowColor: "var(--menu-background, #fff)", offset: this.props.offset, children: (0, import_jsx_runtime15.jsxs)("div", { className: `deck-widget-popup-content ${this.props.className}`, style, children: [this.props.closeButton && (0, import_jsx_runtime15.jsx)("div", { className: "deck-widget-popup-controls", style: { width: "100%", display: "flex", justifyContent: "end" }, children: (0, import_jsx_runtime15.jsx)(IconButton, { className: "deck-widget-popup-close-button", onClick: () => this._setIsOpen(false) }) }), (0, import_jsx_runtime15.jsx)(UserContent, { ...typeof content === "string" ? { text: content } : content })] }) })] });
5147
+ const ui = (0, import_jsx_runtime16.jsxs)("div", { children: [marker && (0, import_jsx_runtime16.jsx)("div", { className: "deck-widget-popup-marker", style: { left: x, top: y }, children: (0, import_jsx_runtime16.jsx)(UserContent, { ...marker, onClick: () => this._setIsOpen(true) }) }), this.isOpen && (0, import_jsx_runtime16.jsx)(Popover, { x, y, placement: this.props.placement, arrow: this.props.arrow, arrowColor: "var(--menu-background, #fff)", offset: this.props.offset, children: (0, import_jsx_runtime16.jsxs)("div", { className: `deck-widget-popup-content ${this.props.className}`, style, children: [this.props.closeButton && (0, import_jsx_runtime16.jsx)("div", { className: "deck-widget-popup-controls", style: { width: "100%", display: "flex", justifyContent: "end" }, children: (0, import_jsx_runtime16.jsx)(IconButton, { className: "deck-widget-popup-close-button", onClick: () => this._setIsOpen(false) }) }), (0, import_jsx_runtime16.jsx)(UserContent, { ...typeof content === "string" ? { text: content } : content })] }) })] });
1934
5148
  (0, import_preact10.render)(ui, rootElement);
1935
5149
  }
1936
5150
  };
1937
5151
  PopupWidget.defaultProps = {
1938
- ...import_core12.Widget.defaultProps,
5152
+ ...Widget.defaultProps,
1939
5153
  id: "info",
1940
5154
  viewId: null,
1941
5155
  position: [0, 0],
@@ -1952,10 +5166,9 @@ PopupWidget.defaultProps = {
1952
5166
  };
1953
5167
 
1954
5168
  // dist/context-menu-widget.js
1955
- var import_jsx_runtime16 = require("preact/jsx-runtime");
1956
- var import_core13 = require("@deck.gl/core");
5169
+ var import_jsx_runtime17 = require("preact/jsx-runtime");
1957
5170
  var import_preact11 = require("preact");
1958
- var ContextMenuWidget = class extends import_core13.Widget {
5171
+ var ContextMenuWidget = class extends Widget {
1959
5172
  constructor(props) {
1960
5173
  super(props);
1961
5174
  this.className = "deck-widget-context-menu";
@@ -2000,7 +5213,7 @@ var ContextMenuWidget = class extends import_core13.Widget {
2000
5213
  position: "static",
2001
5214
  ...this.props.style
2002
5215
  };
2003
- const ui = (0, import_jsx_runtime16.jsx)(Popover, { x: pickInfo.x, y: pickInfo.y, placement: this.props.placement, arrow: this.props.arrow, arrowColor: "var(--menu-background, #fff)", offset: this.props.offset, children: (0, import_jsx_runtime16.jsx)(SimpleMenu, { menuItems: items, onSelect: (value) => this.props.onMenuItemSelected(value, pickInfo), style, isOpen: true, onClose: () => this.hide() }) });
5216
+ const ui = (0, import_jsx_runtime17.jsx)(Popover, { x: pickInfo.x, y: pickInfo.y, placement: this.props.placement, arrow: this.props.arrow, arrowColor: "var(--menu-background, #fff)", offset: this.props.offset, children: (0, import_jsx_runtime17.jsx)(SimpleMenu, { menuItems: items, onSelect: (value) => this.props.onMenuItemSelected(value, pickInfo), style, isOpen: true, onClose: () => this.hide() }) });
2004
5217
  (0, import_preact11.render)(ui, rootElement);
2005
5218
  }
2006
5219
  hide() {
@@ -2009,7 +5222,7 @@ var ContextMenuWidget = class extends import_core13.Widget {
2009
5222
  }
2010
5223
  };
2011
5224
  ContextMenuWidget.defaultProps = {
2012
- ...import_core13.Widget.defaultProps,
5225
+ ...Widget.defaultProps,
2013
5226
  id: "context",
2014
5227
  viewId: null,
2015
5228
  menuItems: [],
@@ -2022,15 +5235,14 @@ ContextMenuWidget.defaultProps = {
2022
5235
  };
2023
5236
 
2024
5237
  // dist/scrollbar-widget.js
2025
- var import_jsx_runtime18 = require("preact/jsx-runtime");
2026
- var import_core14 = require("@deck.gl/core");
5238
+ var import_jsx_runtime19 = require("preact/jsx-runtime");
2027
5239
  var import_preact12 = require("preact");
2028
5240
 
2029
5241
  // dist/lib/components/range-input.js
2030
- var import_jsx_runtime17 = require("preact/jsx-runtime");
2031
- var import_hooks6 = require("preact/hooks");
5242
+ var import_jsx_runtime18 = require("preact/jsx-runtime");
5243
+ var import_hooks7 = require("preact/hooks");
2032
5244
  var wheelListenerOptions = { passive: false };
2033
- var clamp2 = (value, min, max) => {
5245
+ var clamp6 = (value, min, max) => {
2034
5246
  if (value < min) {
2035
5247
  return min;
2036
5248
  }
@@ -2064,16 +5276,16 @@ var getEffectivePage = (pageSize, rangeSize) => {
2064
5276
  function RangeInput(props) {
2065
5277
  const { className = "", min, max, step, value, orientation, pageSize, stepButtons = false, startButtonAriaLabel, endButtonAriaLabel, eventTarget, decorations = [], onChange } = props;
2066
5278
  const vertical = orientation !== "horizontal";
2067
- const rootRef = (0, import_hooks6.useRef)(null);
2068
- const trackRef = (0, import_hooks6.useRef)(null);
2069
- const thumbRef = (0, import_hooks6.useRef)(null);
2070
- const dragStateRef = (0, import_hooks6.useRef)(null);
2071
- const [trackLength, setTrackLength] = (0, import_hooks6.useState)(0);
5279
+ const rootRef = (0, import_hooks7.useRef)(null);
5280
+ const trackRef = (0, import_hooks7.useRef)(null);
5281
+ const thumbRef = (0, import_hooks7.useRef)(null);
5282
+ const dragStateRef = (0, import_hooks7.useRef)(null);
5283
+ const [trackLength, setTrackLength] = (0, import_hooks7.useState)(0);
2072
5284
  const range = max - min;
2073
5285
  const rangeSize = Math.max(0, value[1] - value[0]);
2074
5286
  const maxStart = Math.max(0, range - rangeSize);
2075
- const clampedStart = clamp2(value[0], min, min + maxStart);
2076
- const { thumbLength, thumbOffset } = (0, import_hooks6.useMemo)(() => {
5287
+ const clampedStart = clamp6(value[0], min, min + maxStart);
5288
+ const { thumbLength, thumbOffset } = (0, import_hooks7.useMemo)(() => {
2077
5289
  if (trackLength <= 0 || range <= 0) {
2078
5290
  return { thumbLength: 0, thumbOffset: 0 };
2079
5291
  }
@@ -2082,28 +5294,28 @@ function RangeInput(props) {
2082
5294
  }
2083
5295
  const nextThumbLength = rangeSize / range;
2084
5296
  const travel = Math.max(0, 1 - nextThumbLength);
2085
- const ratio = maxStart <= 0 ? 0 : clamp2((clampedStart - min) / maxStart, 0, 1);
5297
+ const ratio = maxStart <= 0 ? 0 : clamp6((clampedStart - min) / maxStart, 0, 1);
2086
5298
  return {
2087
5299
  thumbLength: Math.max(0, Math.min(nextThumbLength, 1)),
2088
5300
  thumbOffset: travel * ratio
2089
5301
  };
2090
5302
  }, [trackLength, range, rangeSize, maxStart, clampedStart, min]);
2091
- const emitRange = (0, import_hooks6.useCallback)((nextStart) => {
5303
+ const emitRange = (0, import_hooks7.useCallback)((nextStart) => {
2092
5304
  if (!onChange) {
2093
5305
  return;
2094
5306
  }
2095
- const clamped = clamp2(nextStart, min, min + maxStart);
5307
+ const clamped = clamp6(nextStart, min, min + maxStart);
2096
5308
  onChange([clamped, clamped + rangeSize]);
2097
5309
  }, [onChange, min, maxStart, rangeSize]);
2098
- const handleStepNegative = (0, import_hooks6.useCallback)((event) => {
5310
+ const handleStepNegative = (0, import_hooks7.useCallback)((event) => {
2099
5311
  event.stopPropagation();
2100
5312
  emitRange(clampedStart - getEffectiveStep(step, range));
2101
5313
  }, [emitRange, clampedStart, step, range]);
2102
- const handleStepPositive = (0, import_hooks6.useCallback)((event) => {
5314
+ const handleStepPositive = (0, import_hooks7.useCallback)((event) => {
2103
5315
  event.stopPropagation();
2104
5316
  emitRange(clampedStart + getEffectiveStep(step, range));
2105
5317
  }, [emitRange, clampedStart, step, range]);
2106
- const handleTrackClick = (0, import_hooks6.useCallback)((event) => {
5318
+ const handleTrackClick = (0, import_hooks7.useCallback)((event) => {
2107
5319
  if (event.button !== 0) {
2108
5320
  return;
2109
5321
  }
@@ -2121,7 +5333,7 @@ function RangeInput(props) {
2121
5333
  const coordinate = vertical ? event.clientY - trackStart : event.clientX - trackStart;
2122
5334
  const span = Math.max(1, 1 - thumbLength) * trackLength;
2123
5335
  const thumbCenter = thumbLength / 2 * trackLength;
2124
- const ratio = span <= 0 ? 0 : clamp2((coordinate - thumbCenter) / span, 0, 1);
5336
+ const ratio = span <= 0 ? 0 : clamp6((coordinate - thumbCenter) / span, 0, 1);
2125
5337
  emitRange(min + ratio * maxStart);
2126
5338
  }, [vertical, trackLength, thumbLength, emitRange, min, maxStart]);
2127
5339
  const handleThumbPointerDown = (event) => {
@@ -2144,7 +5356,7 @@ function RangeInput(props) {
2144
5356
  event.preventDefault();
2145
5357
  event.stopPropagation();
2146
5358
  };
2147
- const handleThumbPointerMove = (0, import_hooks6.useCallback)((event) => {
5359
+ const handleThumbPointerMove = (0, import_hooks7.useCallback)((event) => {
2148
5360
  const state = dragStateRef.current;
2149
5361
  if (!state || state.pointerId !== event.pointerId) {
2150
5362
  return;
@@ -2153,11 +5365,11 @@ function RangeInput(props) {
2153
5365
  const coordinate = vertical ? event.clientY : event.clientX;
2154
5366
  const delta = coordinate - state.startCoord;
2155
5367
  const ratio = state.startRatio + delta / trackLength2;
2156
- const nextStart = clamp2(ratio * (state.max - state.min), 0, state.maxStart) + state.min;
5368
+ const nextStart = clamp6(ratio * (state.max - state.min), 0, state.maxStart) + state.min;
2157
5369
  emitRange(nextStart);
2158
5370
  event.preventDefault();
2159
5371
  }, [emitRange, vertical]);
2160
- const handleThumbPointerUp = (0, import_hooks6.useCallback)((event) => {
5372
+ const handleThumbPointerUp = (0, import_hooks7.useCallback)((event) => {
2161
5373
  var _a;
2162
5374
  const state = dragStateRef.current;
2163
5375
  if (state && state.pointerId === event.pointerId) {
@@ -2166,7 +5378,7 @@ function RangeInput(props) {
2166
5378
  event.preventDefault();
2167
5379
  }
2168
5380
  }, []);
2169
- const handleKeyDown = (0, import_hooks6.useCallback)((event) => {
5381
+ const handleKeyDown = (0, import_hooks7.useCallback)((event) => {
2170
5382
  switch (event.key) {
2171
5383
  case "ArrowUp":
2172
5384
  case "ArrowLeft":
@@ -2202,7 +5414,7 @@ function RangeInput(props) {
2202
5414
  break;
2203
5415
  }
2204
5416
  }, [vertical, emitRange, clampedStart, step, range, pageSize, rangeSize, min, maxStart]);
2205
- const handleWheel = (0, import_hooks6.useCallback)((event) => {
5417
+ const handleWheel = (0, import_hooks7.useCallback)((event) => {
2206
5418
  event.preventDefault();
2207
5419
  event.stopPropagation();
2208
5420
  if (maxStart === 0) {
@@ -2222,10 +5434,10 @@ function RangeInput(props) {
2222
5434
  }
2223
5435
  emitRange(clampedStart + delta);
2224
5436
  }, [emitRange, clampedStart, maxStart, vertical, step, range, pageSize, rangeSize]);
2225
- (0, import_hooks6.useLayoutEffect)(() => {
5437
+ (0, import_hooks7.useLayoutEffect)(() => {
2226
5438
  setTrackLength(getTrackDimension(trackRef.current, vertical)[1]);
2227
5439
  }, [vertical]);
2228
- (0, import_hooks6.useLayoutEffect)(() => {
5440
+ (0, import_hooks7.useLayoutEffect)(() => {
2229
5441
  const track = trackRef.current;
2230
5442
  if (!track) {
2231
5443
  return void 0;
@@ -2245,7 +5457,7 @@ function RangeInput(props) {
2245
5457
  }
2246
5458
  return void 0;
2247
5459
  }, [vertical]);
2248
- (0, import_hooks6.useEffect)(() => {
5460
+ (0, import_hooks7.useEffect)(() => {
2249
5461
  const eventRoot = eventTarget ?? rootRef.current;
2250
5462
  if (!eventRoot)
2251
5463
  return void 0;
@@ -2256,7 +5468,7 @@ function RangeInput(props) {
2256
5468
  eventRoot.removeEventListener("wheel", handleWheel, wheelListenerOptions);
2257
5469
  };
2258
5470
  }, [eventTarget, handleKeyDown, handleWheel]);
2259
- const decorationElements = (0, import_hooks6.useMemo)(() => {
5471
+ const decorationElements = (0, import_hooks7.useMemo)(() => {
2260
5472
  if (!decorations.length || range <= 0) {
2261
5473
  return [];
2262
5474
  }
@@ -2277,14 +5489,14 @@ function RangeInput(props) {
2277
5489
  left: `${offsetPct}%`,
2278
5490
  width: `${sizePct}%`
2279
5491
  };
2280
- return (0, import_jsx_runtime17.jsx)("div", { className: "deck-widget-range__decoration", style, children: decoration.element }, `decoration-${index}`);
5492
+ return (0, import_jsx_runtime18.jsx)("div", { className: "deck-widget-range__decoration", style, children: decoration.element }, `decoration-${index}`);
2281
5493
  });
2282
5494
  }, [decorations, range, min, vertical]);
2283
- return (0, import_jsx_runtime17.jsxs)("div", { ref: rootRef, tabIndex: 0, role: "scrollbar", "aria-valuemin": min, "aria-valuemax": min + maxStart, "aria-valuenow": clampedStart, "aria-orientation": orientation, className: `${className} deck-widget-range deck-widget-range--${orientation} ${maxStart === 0 ? "deck-widget-range--disabled" : ""}`, children: [stepButtons && (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "deck-widget-range__button deck-widget-range__button--start", "aria-label": startButtonAriaLabel, disabled: clampedStart <= min, onClick: handleStepNegative, children: (0, import_jsx_runtime17.jsx)("span", { className: "deck-widget-icon" }) }), (0, import_jsx_runtime17.jsxs)("div", { className: "deck-widget-range__track", ref: trackRef, onClick: handleTrackClick, children: [(0, import_jsx_runtime17.jsx)("div", { className: "deck-widget-range__decorations", children: decorationElements }), (0, import_jsx_runtime17.jsx)("div", { className: "deck-widget-range__thumb", "data-scrollbar-thumb": "true", ref: thumbRef, style: vertical ? { height: `${thumbLength * 100}%`, top: `${thumbOffset * 100}%` } : { width: `${thumbLength * 100}%`, left: `${thumbOffset * 100}%` }, onPointerDown: handleThumbPointerDown, onPointerMove: handleThumbPointerMove, onPointerUp: handleThumbPointerUp, onPointerCancel: handleThumbPointerUp })] }), stepButtons && (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "deck-widget-range__button deck-widget-range__button--end", "aria-label": endButtonAriaLabel, disabled: clampedStart >= min + maxStart, onClick: handleStepPositive, children: (0, import_jsx_runtime17.jsx)("span", { className: "deck-widget-icon" }) })] });
5495
+ return (0, import_jsx_runtime18.jsxs)("div", { ref: rootRef, tabIndex: 0, role: "scrollbar", "aria-valuemin": min, "aria-valuemax": min + maxStart, "aria-valuenow": clampedStart, "aria-orientation": orientation, className: `${className} deck-widget-range deck-widget-range--${orientation} ${maxStart === 0 ? "deck-widget-range--disabled" : ""}`, children: [stepButtons && (0, import_jsx_runtime18.jsx)("button", { type: "button", className: "deck-widget-range__button deck-widget-range__button--start", "aria-label": startButtonAriaLabel, disabled: clampedStart <= min, onClick: handleStepNegative, children: (0, import_jsx_runtime18.jsx)("span", { className: "deck-widget-icon" }) }), (0, import_jsx_runtime18.jsxs)("div", { className: "deck-widget-range__track", ref: trackRef, onClick: handleTrackClick, children: [(0, import_jsx_runtime18.jsx)("div", { className: "deck-widget-range__decorations", children: decorationElements }), (0, import_jsx_runtime18.jsx)("div", { className: "deck-widget-range__thumb", "data-scrollbar-thumb": "true", ref: thumbRef, style: vertical ? { height: `${thumbLength * 100}%`, top: `${thumbOffset * 100}%` } : { width: `${thumbLength * 100}%`, left: `${thumbOffset * 100}%` }, onPointerDown: handleThumbPointerDown, onPointerMove: handleThumbPointerMove, onPointerUp: handleThumbPointerUp, onPointerCancel: handleThumbPointerUp })] }), stepButtons && (0, import_jsx_runtime18.jsx)("button", { type: "button", className: "deck-widget-range__button deck-widget-range__button--end", "aria-label": endButtonAriaLabel, disabled: clampedStart >= min + maxStart, onClick: handleStepPositive, children: (0, import_jsx_runtime18.jsx)("span", { className: "deck-widget-icon" }) })] });
2284
5496
  }
2285
5497
 
2286
5498
  // dist/scrollbar-widget.js
2287
- var clamp3 = (value, min, max) => {
5499
+ var clamp7 = (value, min, max) => {
2288
5500
  if (value < min) {
2289
5501
  return min;
2290
5502
  }
@@ -2293,10 +5505,10 @@ var clamp3 = (value, min, max) => {
2293
5505
  }
2294
5506
  return value;
2295
5507
  };
2296
- var ScrollbarWidget = class extends import_core14.Widget {
5508
+ var ScrollbarWidget = class _ScrollbarWidget extends Widget {
2297
5509
  constructor(props) {
2298
5510
  const resolved = {
2299
- ...ScrollbarWidget.defaultProps,
5511
+ ..._ScrollbarWidget.defaultProps,
2300
5512
  ...props
2301
5513
  };
2302
5514
  super(resolved);
@@ -2328,7 +5540,7 @@ var ScrollbarWidget = class extends import_core14.Widget {
2328
5540
  const isVertical = this.isVertical();
2329
5541
  const startLabel = this.props.startButtonAriaLabel ?? (isVertical ? "Scroll up" : "Scroll left");
2330
5542
  const endLabel = this.props.endButtonAriaLabel ?? (isVertical ? "Scroll down" : "Scroll right");
2331
- const ui = (0, import_jsx_runtime18.jsx)(RangeInput, { min: 0, max: Math.max(0, this.contentSize), step: this.getEffectiveStep(), pageSize: this.getEffectivePage(), value: [clampedOffset, clampedOffset + this.viewportSize], orientation: this.props.orientation, stepButtons: true, startButtonAriaLabel: startLabel, endButtonAriaLabel: endLabel, eventTarget: wheelTarget, decorations, onChange: this.handleRangeChange });
5543
+ const ui = (0, import_jsx_runtime19.jsx)(RangeInput, { min: 0, max: Math.max(0, this.contentSize), step: this.getEffectiveStep(), pageSize: this.getEffectivePage(), value: [clampedOffset, clampedOffset + this.viewportSize], orientation: this.props.orientation, stepButtons: true, startButtonAriaLabel: startLabel, endButtonAriaLabel: endLabel, eventTarget: wheelTarget, decorations, onChange: this.handleRangeChange });
2332
5544
  (0, import_preact12.render)(ui, element);
2333
5545
  }
2334
5546
  onRemove() {
@@ -2341,6 +5553,10 @@ var ScrollbarWidget = class extends import_core14.Widget {
2341
5553
  var _a, _b, _c;
2342
5554
  return this.props.contentBounds ?? ((_c = (_b = (_a = this.deck) == null ? void 0 : _a.getView(viewId)) == null ? void 0 : _b.controller) == null ? void 0 : _c.maxBounds) ?? null;
2343
5555
  }
5556
+ getContentBoundsPadding(viewId) {
5557
+ var _a, _b, _c;
5558
+ return this.props.contentBoundsPadding ?? ((_c = (_b = (_a = this.deck) == null ? void 0 : _a.getView(viewId)) == null ? void 0 : _b.controller) == null ? void 0 : _c.maxBoundsPadding) ?? null;
5559
+ }
2344
5560
  updateViewport(viewport) {
2345
5561
  if (!viewport) {
2346
5562
  this.contentSize = 0;
@@ -2351,9 +5567,12 @@ var ScrollbarWidget = class extends import_core14.Widget {
2351
5567
  const contentBounds = this.getContentBounds(viewport.id);
2352
5568
  const isVertical = this.isVertical();
2353
5569
  const projectedBounds = contentBounds ? projectBounds(contentBounds, viewport, isVertical) : [0, 0];
5570
+ const contentRect = getMaxBoundsRect(viewport.width, viewport.height, this.getContentBoundsPadding(viewport.id));
5571
+ const contentStart = isVertical ? contentRect.y : contentRect.x;
5572
+ const viewportSize = isVertical ? contentRect.height : contentRect.width;
2354
5573
  this.contentSize = projectedBounds[1] - projectedBounds[0];
2355
- this.scrollOffset = -projectedBounds[0];
2356
- this.viewportSize = isVertical ? viewport.height : viewport.width;
5574
+ this.scrollOffset = contentStart - projectedBounds[0];
5575
+ this.viewportSize = Math.max(0, viewportSize);
2357
5576
  }
2358
5577
  getDecorations(viewport) {
2359
5578
  const { decorations = [] } = this.props;
@@ -2378,7 +5597,7 @@ var ScrollbarWidget = class extends import_core14.Widget {
2378
5597
  } : void 0;
2379
5598
  return {
2380
5599
  position: [start - contentStart, end - contentStart],
2381
- element: (0, import_jsx_runtime18.jsx)("div", { style: {
5600
+ element: (0, import_jsx_runtime19.jsx)("div", { style: {
2382
5601
  pointerEvents: onClick ? "all" : "none",
2383
5602
  width: "100%",
2384
5603
  height: "100%",
@@ -2400,7 +5619,7 @@ var ScrollbarWidget = class extends import_core14.Widget {
2400
5619
  }
2401
5620
  getClampedOffset() {
2402
5621
  const maxScroll = this.getMaxScroll();
2403
- return clamp3(this.scrollOffset, 0, maxScroll);
5622
+ return clamp7(this.scrollOffset, 0, maxScroll);
2404
5623
  }
2405
5624
  isVertical() {
2406
5625
  return this.props.orientation !== "horizontal";
@@ -2419,7 +5638,7 @@ var ScrollbarWidget = class extends import_core14.Widget {
2419
5638
  }
2420
5639
  emitScroll(next) {
2421
5640
  const maxScroll = this.getMaxScroll();
2422
- const target = clamp3(Math.round(next), 0, maxScroll);
5641
+ const target = clamp7(Math.round(next), 0, maxScroll);
2423
5642
  const viewport = this.viewport;
2424
5643
  if (viewport && target !== this.getClampedOffset()) {
2425
5644
  const pixel = viewport.project(viewport.position);
@@ -2441,8 +5660,9 @@ var ScrollbarWidget = class extends import_core14.Widget {
2441
5660
  }
2442
5661
  };
2443
5662
  ScrollbarWidget.defaultProps = {
2444
- ...import_core14.Widget.defaultProps,
5663
+ ...Widget.defaultProps,
2445
5664
  contentBounds: null,
5665
+ contentBoundsPadding: null,
2446
5666
  placement: "top-right",
2447
5667
  viewId: null,
2448
5668
  orientation: "vertical",
@@ -2463,10 +5683,9 @@ function projectBounds(bounds, viewport, isVertical) {
2463
5683
  }
2464
5684
 
2465
5685
  // dist/icon-widget.js
2466
- var import_jsx_runtime19 = require("preact/jsx-runtime");
5686
+ var import_jsx_runtime20 = require("preact/jsx-runtime");
2467
5687
  var import_preact13 = require("preact");
2468
- var import_core15 = require("@deck.gl/core");
2469
- var IconWidget = class extends import_core15.Widget {
5688
+ var IconWidget = class extends Widget {
2470
5689
  constructor(props) {
2471
5690
  super(props);
2472
5691
  this.className = "";
@@ -2479,26 +5698,26 @@ var IconWidget = class extends import_core15.Widget {
2479
5698
  super.setProps(props);
2480
5699
  }
2481
5700
  onRenderHTML(rootElement) {
2482
- const { className, style, icon, color, label, onClick } = this.props;
2483
- (0, import_preact13.render)((0, import_jsx_runtime19.jsx)(IconButton, { className, style, color, icon, label, onClick }), rootElement);
5701
+ const { className, style, icon, color, label, tooltip, onClick } = this.props;
5702
+ (0, import_preact13.render)((0, import_jsx_runtime20.jsx)(IconButton, { className, style, color, icon, label, tooltip, onClick }), rootElement);
2484
5703
  }
2485
5704
  };
2486
5705
  IconWidget.defaultProps = {
2487
- ...import_core15.Widget.defaultProps,
5706
+ ...Widget.defaultProps,
2488
5707
  id: "icon",
2489
5708
  placement: "top-left",
2490
5709
  viewId: null,
2491
5710
  icon: "",
2492
5711
  label: "",
5712
+ tooltip: void 0,
2493
5713
  color: "",
2494
5714
  onClick: void 0
2495
5715
  };
2496
5716
 
2497
5717
  // dist/toggle-widget.js
2498
- var import_jsx_runtime20 = require("preact/jsx-runtime");
5718
+ var import_jsx_runtime21 = require("preact/jsx-runtime");
2499
5719
  var import_preact14 = require("preact");
2500
- var import_core16 = require("@deck.gl/core");
2501
- var ToggleWidget = class extends import_core16.Widget {
5720
+ var ToggleWidget = class extends Widget {
2502
5721
  constructor(props) {
2503
5722
  super(props);
2504
5723
  this.className = "deck-widget-toggle";
@@ -2518,14 +5737,14 @@ var ToggleWidget = class extends import_core16.Widget {
2518
5737
  super.setProps(props);
2519
5738
  }
2520
5739
  onRenderHTML(rootElement) {
2521
- const { className, style, icon, label, color, onIcon = icon, onLabel = label, onColor = color } = this.props;
5740
+ const { className, style, icon, label, tooltip, color, onIcon = icon, onLabel = label, onTooltip, onColor = color } = this.props;
2522
5741
  const on = this.checked;
2523
5742
  rootElement.dataset.checked = String(on);
2524
- (0, import_preact14.render)((0, import_jsx_runtime20.jsx)(IconButton, { className, style, icon: on ? onIcon : icon, label: on ? onLabel : label, color: on ? onColor : color, onClick: this._toggle }), rootElement);
5743
+ (0, import_preact14.render)((0, import_jsx_runtime21.jsx)(IconButton, { className, style, icon: on ? onIcon : icon, label: on ? onLabel : label, tooltip: on ? onTooltip : tooltip, color: on ? onColor : color, onClick: this._toggle }), rootElement);
2525
5744
  }
2526
5745
  };
2527
5746
  ToggleWidget.defaultProps = {
2528
- ...import_core16.Widget.defaultProps,
5747
+ ...Widget.defaultProps,
2529
5748
  id: "icon",
2530
5749
  placement: "top-left",
2531
5750
  viewId: null,
@@ -2533,17 +5752,18 @@ ToggleWidget.defaultProps = {
2533
5752
  icon: "",
2534
5753
  onIcon: void 0,
2535
5754
  label: "",
5755
+ tooltip: void 0,
2536
5756
  onLabel: void 0,
5757
+ onTooltip: void 0,
2537
5758
  color: "",
2538
5759
  onColor: void 0,
2539
5760
  onChange: void 0
2540
5761
  };
2541
5762
 
2542
5763
  // dist/selector-widget.js
2543
- var import_jsx_runtime21 = require("preact/jsx-runtime");
5764
+ var import_jsx_runtime22 = require("preact/jsx-runtime");
2544
5765
  var import_preact15 = require("preact");
2545
- var import_core17 = require("@deck.gl/core");
2546
- var SelectorWidget = class extends import_core17.Widget {
5766
+ var SelectorWidget = class extends Widget {
2547
5767
  constructor(props) {
2548
5768
  super(props);
2549
5769
  this.className = "deck-widget-selector";
@@ -2576,25 +5796,25 @@ var SelectorWidget = class extends import_core17.Widget {
2576
5796
  }
2577
5797
  onRenderHTML(rootElement) {
2578
5798
  const selectedOption = this.props.options.find((opt) => opt.value === this.value) ?? this.props.options[0];
2579
- (0, import_preact15.render)((0, import_jsx_runtime21.jsxs)("div", { children: [(0, import_jsx_runtime21.jsx)(IconButton, { icon: selectedOption.icon, label: selectedOption.label, onClick: this._toggleMenu }), this.isOpen && (0, import_jsx_runtime21.jsx)(Popover, { ...this.isOpen, children: (0, import_jsx_runtime21.jsx)(SimpleMenu, { isOpen: true, style: { pointerEvents: "auto", position: "static" }, menuItems: this.props.options, onSelect: this._handleSelectMode, onClose: this._toggleMenu }) })] }), rootElement);
5799
+ (0, import_preact15.render)((0, import_jsx_runtime22.jsxs)("div", { children: [(0, import_jsx_runtime22.jsx)(IconButton, { icon: selectedOption.icon, label: selectedOption.label, tooltip: this.props.tooltip, onClick: this._toggleMenu }), this.isOpen && (0, import_jsx_runtime22.jsx)(Popover, { ...this.isOpen, children: (0, import_jsx_runtime22.jsx)(SimpleMenu, { isOpen: true, style: { pointerEvents: "auto", position: "static" }, menuItems: this.props.options, onSelect: this._handleSelectMode, onClose: this._toggleMenu }) })] }), rootElement);
2580
5800
  }
2581
5801
  };
2582
5802
  SelectorWidget.defaultProps = {
2583
- ...import_core17.Widget.defaultProps,
5803
+ ...Widget.defaultProps,
2584
5804
  id: "view-selector",
2585
5805
  placement: "top-left",
2586
5806
  viewId: null,
2587
5807
  initialValue: "",
2588
5808
  options: [],
5809
+ tooltip: void 0,
2589
5810
  onChange: () => {
2590
5811
  }
2591
5812
  };
2592
5813
 
2593
5814
  // dist/timeline-widget.js
2594
- var import_jsx_runtime22 = require("preact/jsx-runtime");
2595
- var import_core18 = require("@deck.gl/core");
5815
+ var import_jsx_runtime23 = require("preact/jsx-runtime");
2596
5816
  var import_preact16 = require("preact");
2597
- var TimelineWidget = class extends import_core18.Widget {
5817
+ var TimelineWidget = class extends Widget {
2598
5818
  /**
2599
5819
  * Returns the current time value.
2600
5820
  * In controlled mode, returns the time prop.
@@ -2708,14 +5928,14 @@ var TimelineWidget = class extends import_core18.Widget {
2708
5928
  this.stop();
2709
5929
  }
2710
5930
  onRenderHTML(rootElement) {
2711
- const { timeRange, step, formatLabel } = this.props;
5931
+ const { timeRange, step, formatLabel, playTooltip, pauseTooltip } = this.props;
2712
5932
  const isPlaying = this.getPlaying();
2713
5933
  const currentTime = this.getTime();
2714
5934
  rootElement.dataset.placement = this.props.placement;
2715
- (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: [
5935
+ (0, import_preact16.render)((0, import_jsx_runtime23.jsxs)("div", { className: "deck-widget-button-group", children: [isPlaying ? (0, import_jsx_runtime23.jsx)(IconButton, { label: "Pause", tooltip: pauseTooltip, className: "deck-widget-timeline-pause", onClick: this.handlePlayPause }) : (0, import_jsx_runtime23.jsx)(IconButton, { label: "Play", tooltip: playTooltip, className: "deck-widget-timeline-play", onClick: this.handlePlayPause }), (0, import_jsx_runtime23.jsx)(RangeInput, { min: timeRange[0], max: timeRange[1], orientation: "horizontal", step, value: [currentTime, currentTime], onChange: this.handleTimeChange, decorations: [
2716
5936
  {
2717
5937
  position: [currentTime, currentTime + step],
2718
- element: (0, import_jsx_runtime22.jsx)("div", { className: "deck-widget-timeline-label deck-widget-timeline-label--current", children: formatLabel(currentTime) })
5938
+ element: (0, import_jsx_runtime23.jsx)("div", { className: "deck-widget-timeline-label deck-widget-timeline-label--current", children: formatLabel(currentTime) })
2719
5939
  }
2720
5940
  ] })] }), rootElement);
2721
5941
  }
@@ -2750,7 +5970,7 @@ var TimelineWidget = class extends import_core18.Widget {
2750
5970
  }
2751
5971
  };
2752
5972
  TimelineWidget.defaultProps = {
2753
- ...import_core18.Widget.defaultProps,
5973
+ ...Widget.defaultProps,
2754
5974
  id: "timeline",
2755
5975
  placement: "bottom-left",
2756
5976
  viewId: null,
@@ -2767,14 +5987,15 @@ TimelineWidget.defaultProps = {
2767
5987
  playing: void 0,
2768
5988
  onPlayingChange: () => {
2769
5989
  },
2770
- formatLabel: String
5990
+ formatLabel: String,
5991
+ playTooltip: void 0,
5992
+ pauseTooltip: void 0
2771
5993
  };
2772
5994
 
2773
5995
  // dist/screenshot-widget.js
2774
- var import_jsx_runtime23 = require("preact/jsx-runtime");
5996
+ var import_jsx_runtime24 = require("preact/jsx-runtime");
2775
5997
  var import_preact17 = require("preact");
2776
- var import_core19 = require("@deck.gl/core");
2777
- var ScreenshotWidget = class extends import_core19.Widget {
5998
+ var ScreenshotWidget = class extends Widget {
2778
5999
  constructor(props = {}) {
2779
6000
  super(props);
2780
6001
  this.className = "deck-widget-screenshot";
@@ -2787,7 +6008,7 @@ var ScreenshotWidget = class extends import_core19.Widget {
2787
6008
  super.setProps(props);
2788
6009
  }
2789
6010
  onRenderHTML(rootElement) {
2790
- (0, import_preact17.render)((0, import_jsx_runtime23.jsx)(IconButton, { className: "deck-widget-camera", label: this.props.label, onClick: this.handleClick.bind(this) }), rootElement);
6011
+ (0, import_preact17.render)((0, import_jsx_runtime24.jsx)(IconButton, { className: "deck-widget-camera", label: this.props.label, tooltip: this.props.tooltip, onClick: this.handleClick.bind(this) }), rootElement);
2791
6012
  }
2792
6013
  handleClick() {
2793
6014
  if (this.props.onCapture) {
@@ -2814,26 +6035,26 @@ var ScreenshotWidget = class extends import_core19.Widget {
2814
6035
  }
2815
6036
  };
2816
6037
  ScreenshotWidget.defaultProps = {
2817
- ...import_core19.Widget.defaultProps,
6038
+ ...Widget.defaultProps,
2818
6039
  id: "screenshot",
2819
6040
  placement: "top-left",
2820
6041
  viewId: null,
2821
6042
  label: "Screenshot",
6043
+ tooltip: void 0,
2822
6044
  filename: "screenshot.png",
2823
6045
  imageFormat: "image/png",
2824
6046
  onCapture: void 0
2825
6047
  };
2826
6048
 
2827
6049
  // dist/theme-widget.js
2828
- var import_jsx_runtime24 = require("preact/jsx-runtime");
2829
- var import_core20 = require("@deck.gl/core");
2830
- var import_core21 = require("@deck.gl/core");
6050
+ var import_jsx_runtime25 = require("preact/jsx-runtime");
2831
6051
  var import_preact18 = require("preact");
2832
6052
 
2833
6053
  // dist/themes.js
2834
6054
  var LightTheme = {
2835
6055
  "--widget-margin": "12px",
2836
6056
  "--button-size": "28px",
6057
+ "--icon-size": "75%",
2837
6058
  "--button-corner-radius": "8px",
2838
6059
  "--button-background": "#fff",
2839
6060
  "--button-stroke": "rgba(255, 255, 255, 0.3)",
@@ -2862,6 +6083,7 @@ var LightTheme = {
2862
6083
  var DarkTheme = {
2863
6084
  "--widget-margin": "12px",
2864
6085
  "--button-size": "28px",
6086
+ "--icon-size": "75%",
2865
6087
  "--button-corner-radius": "8px",
2866
6088
  "--button-background": "rgba(18, 18, 20, 1)",
2867
6089
  "--button-stroke": "rgba(18, 18, 20, 0.30)",
@@ -2890,6 +6112,7 @@ var DarkTheme = {
2890
6112
  var LightGlassTheme = {
2891
6113
  "--widget-margin": "12px",
2892
6114
  "--button-size": "28px",
6115
+ "--icon-size": "75%",
2893
6116
  "--button-corner-radius": "8px",
2894
6117
  "--button-background": "rgba(255, 255, 255, 0.6)",
2895
6118
  "--button-stroke": "rgba(255, 255, 255, 0.3)",
@@ -2918,6 +6141,7 @@ var LightGlassTheme = {
2918
6141
  var DarkGlassTheme = {
2919
6142
  "--widget-margin": "12px",
2920
6143
  "--button-size": "28px",
6144
+ "--icon-size": "75%",
2921
6145
  "--button-corner-radius": "8px",
2922
6146
  "--button-background": "rgba(18, 18, 20, 0.75)",
2923
6147
  "--button-stroke": "rgba(18, 18, 20, 0.30)",
@@ -2945,7 +6169,7 @@ var DarkGlassTheme = {
2945
6169
  };
2946
6170
 
2947
6171
  // dist/theme-widget.js
2948
- var ThemeWidget = class extends import_core21.Widget {
6172
+ var ThemeWidget = class extends Widget {
2949
6173
  constructor(props = {}) {
2950
6174
  super(props);
2951
6175
  this.className = "deck-widget-theme";
@@ -2962,10 +6186,10 @@ var ThemeWidget = class extends import_core21.Widget {
2962
6186
  super.setProps(props);
2963
6187
  }
2964
6188
  onRenderHTML(rootElement) {
2965
- const { lightModeLabel, darkModeLabel } = this.props;
6189
+ const { lightModeLabel, darkModeLabel, lightModeTooltip, darkModeTooltip } = this.props;
2966
6190
  const currentMode = this.getThemeMode();
2967
6191
  this._applyTheme(currentMode, rootElement);
2968
- (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);
6192
+ (0, import_preact18.render)((0, import_jsx_runtime25.jsx)(IconButton, { onClick: this._handleClick.bind(this), label: currentMode === "dark" ? darkModeLabel : lightModeLabel, tooltip: currentMode === "dark" ? darkModeTooltip : lightModeTooltip, className: currentMode === "dark" ? "deck-widget-moon" : "deck-widget-sun" }), rootElement);
2969
6193
  }
2970
6194
  /**
2971
6195
  * Returns the current theme mode.
@@ -2988,16 +6212,16 @@ var ThemeWidget = class extends import_core21.Widget {
2988
6212
  /** Apply theme styling without changing internal state */
2989
6213
  _applyTheme(themeMode, rootElement) {
2990
6214
  const themeStyle = themeMode === "dark" ? this.props.darkModeTheme : this.props.lightModeTheme;
2991
- if ((0, import_core20._deepEqual)(themeStyle, this.appliedTheme, 1)) {
6215
+ if (deepEqual(themeStyle, this.appliedTheme, 1)) {
2992
6216
  return;
2993
6217
  }
2994
6218
  const container = rootElement.closest(".deck-widget-container");
2995
6219
  if (!container)
2996
6220
  return;
2997
- (0, import_core20._applyStyles)(container, themeStyle);
6221
+ applyStyles(container, themeStyle);
2998
6222
  this.appliedTheme = themeStyle;
2999
6223
  const label = themeMode === "dark" ? this.props.darkModeLabel : this.props.lightModeLabel;
3000
- import_core20.log.log(1, `Switched theme to ${label}`, themeStyle)();
6224
+ log_default.log(1, `Switched theme to ${label}`, themeStyle)();
3001
6225
  }
3002
6226
  /** Read browser preference */
3003
6227
  _getInitialThemeMode() {
@@ -3012,13 +6236,15 @@ var ThemeWidget = class extends import_core21.Widget {
3012
6236
  }
3013
6237
  };
3014
6238
  ThemeWidget.defaultProps = {
3015
- ...import_core21.Widget.defaultProps,
6239
+ ...Widget.defaultProps,
3016
6240
  id: "theme",
3017
6241
  placement: "top-left",
3018
6242
  viewId: null,
3019
6243
  lightModeLabel: "Light Mode",
6244
+ lightModeTooltip: void 0,
3020
6245
  lightModeTheme: LightGlassTheme,
3021
6246
  darkModeLabel: "Dark Mode",
6247
+ darkModeTooltip: void 0,
3022
6248
  darkModeTheme: DarkGlassTheme,
3023
6249
  initialThemeMode: "auto",
3024
6250
  themeMode: void 0,
@@ -3027,10 +6253,9 @@ ThemeWidget.defaultProps = {
3027
6253
  };
3028
6254
 
3029
6255
  // dist/loading-widget.js
3030
- var import_jsx_runtime25 = require("preact/jsx-runtime");
6256
+ var import_jsx_runtime26 = require("preact/jsx-runtime");
3031
6257
  var import_preact19 = require("preact");
3032
- var import_core22 = require("@deck.gl/core");
3033
- var LoadingWidget = class extends import_core22.Widget {
6258
+ var LoadingWidget = class extends Widget {
3034
6259
  constructor(props = {}) {
3035
6260
  super(props);
3036
6261
  this.className = "deck-widget-loading";
@@ -3046,7 +6271,7 @@ var LoadingWidget = class extends import_core22.Widget {
3046
6271
  onRenderHTML(rootElement) {
3047
6272
  (0, import_preact19.render)(
3048
6273
  // TODO(ibgreen) - this should not be a button, but styling is so nested that it is easier to reuse this component.
3049
- this.loading && (0, import_jsx_runtime25.jsx)(IconButton, { className: "deck-widget-spinner", label: this.props.label, onClick: this.handleClick.bind(this) }),
6274
+ this.loading && (0, import_jsx_runtime26.jsx)(IconButton, { className: "deck-widget-spinner", label: this.props.label, tooltip: this.props.tooltip, onClick: this.handleClick.bind(this) }),
3050
6275
  rootElement
3051
6276
  );
3052
6277
  }
@@ -3064,21 +6289,21 @@ var LoadingWidget = class extends import_core22.Widget {
3064
6289
  }
3065
6290
  };
3066
6291
  LoadingWidget.defaultProps = {
3067
- ...import_core22.Widget.defaultProps,
6292
+ ...Widget.defaultProps,
3068
6293
  id: "loading",
3069
6294
  placement: "top-left",
3070
6295
  viewId: null,
3071
6296
  label: "Loading layer data",
6297
+ tooltip: void 0,
3072
6298
  onLoadingChange: () => {
3073
6299
  }
3074
6300
  };
3075
6301
 
3076
6302
  // dist/stats-widget.js
3077
- var import_jsx_runtime26 = require("preact/jsx-runtime");
3078
- var import_core23 = require("@deck.gl/core");
3079
- var import_core24 = require("@luma.gl/core");
6303
+ var import_jsx_runtime27 = require("preact/jsx-runtime");
6304
+ var import_core33 = require("@luma.gl/core");
3080
6305
  var import_preact20 = require("preact");
3081
- var import_hooks7 = require("preact/hooks");
6306
+ var import_hooks8 = require("preact/hooks");
3082
6307
  var DEFAULT_COUNT_FORMATTER = (stat) => `${stat.name}: ${stat.count}`;
3083
6308
  function formatTime(time) {
3084
6309
  return time < 1e3 ? `${time.toFixed(2)}ms` : `${(time / 1e3).toFixed(2)}s`;
@@ -3094,7 +6319,7 @@ var DEFAULT_FORMATTERS = {
3094
6319
  fps: (stat) => `${stat.name}: ${Math.round(stat.getHz())}fps`,
3095
6320
  memory: (stat) => `${stat.name}: ${formatMemory(stat.count)}`
3096
6321
  };
3097
- var StatsWidget = class extends import_core23.Widget {
6322
+ var StatsWidget = class extends Widget {
3098
6323
  /**
3099
6324
  * Returns the current expanded state.
3100
6325
  * In controlled mode, returns the expanded prop.
@@ -3149,7 +6374,7 @@ var StatsWidget = class extends import_core23.Widget {
3149
6374
  onRenderHTML(rootElement) {
3150
6375
  const isExpanded = this.getExpanded();
3151
6376
  if (!isExpanded) {
3152
- (0, import_preact20.render)((0, import_jsx_runtime26.jsx)(FpsIcon, { getFps: this._getFps, onClick: this._toggleExpanded }), rootElement);
6377
+ (0, import_preact20.render)((0, import_jsx_runtime27.jsx)(FpsIcon, { getFps: this._getFps, onClick: this._toggleExpanded }), rootElement);
3153
6378
  return;
3154
6379
  }
3155
6380
  const stats = this._getStats();
@@ -3163,11 +6388,11 @@ var StatsWidget = class extends import_core23.Widget {
3163
6388
  stat.reset();
3164
6389
  }
3165
6390
  lines.forEach((line, i) => {
3166
- items.push((0, import_jsx_runtime26.jsx)("div", { style: { whiteSpace: "pre" }, children: line }, `${stat.name}-${i}`));
6391
+ items.push((0, import_jsx_runtime27.jsx)("div", { style: { whiteSpace: "pre" }, children: line }, `${stat.name}-${i}`));
3167
6392
  });
3168
6393
  });
3169
6394
  }
3170
- (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);
6395
+ (0, import_preact20.render)((0, import_jsx_runtime27.jsxs)("div", { className: "deck-widget-stats-container", style: { cursor: "default" }, children: [(0, import_jsx_runtime27.jsxs)("div", { className: "deck-widget-stats-header", style: { cursor: "pointer", pointerEvents: "auto" }, onClick: this._toggleExpanded, children: [(0, import_jsx_runtime27.jsx)("b", { children: title }), deviceLabel && (0, import_jsx_runtime27.jsx)("span", { className: "deck-widget-stats-device", children: deviceLabel }), (0, import_jsx_runtime27.jsx)("button", { className: "deck-widget-dropdown-button", children: (0, import_jsx_runtime27.jsx)("span", { className: "deck-widget-dropdown-icon open" }) })] }), (0, import_jsx_runtime27.jsx)("div", { className: "deck-widget-stats-content", children: items })] }), rootElement);
3171
6396
  }
3172
6397
  onRedraw() {
3173
6398
  if (this.getExpanded()) {
@@ -3184,7 +6409,7 @@ var StatsWidget = class extends import_core23.Widget {
3184
6409
  const metrics = ((_a = this.deck) == null ? void 0 : _a.metrics) ?? {};
3185
6410
  return Object.entries(metrics);
3186
6411
  case "luma":
3187
- return Array.from(import_core24.luma.stats.stats.values())[0];
6412
+ return Array.from(import_core33.luma.stats.stats.values())[0];
3188
6413
  case "device":
3189
6414
  const device = (_b = this.deck) == null ? void 0 : _b.device;
3190
6415
  const stats = device == null ? void 0 : device.statsManager.stats.values();
@@ -3221,7 +6446,7 @@ var StatsWidget = class extends import_core23.Widget {
3221
6446
  }
3222
6447
  };
3223
6448
  StatsWidget.defaultProps = {
3224
- ...import_core23.Widget.defaultProps,
6449
+ ...Widget.defaultProps,
3225
6450
  type: "deck",
3226
6451
  placement: "top-left",
3227
6452
  viewId: null,
@@ -3237,8 +6462,8 @@ StatsWidget.defaultProps = {
3237
6462
  }
3238
6463
  };
3239
6464
  function FpsIcon({ getFps, onClick }) {
3240
- const [fps, setFps] = (0, import_hooks7.useState)(getFps());
3241
- (0, import_hooks7.useEffect)(() => {
6465
+ const [fps, setFps] = (0, import_hooks8.useState)(getFps());
6466
+ (0, import_hooks8.useEffect)(() => {
3242
6467
  const onUpdate = () => {
3243
6468
  setFps(getFps());
3244
6469
  timer = requestAnimationFrame(onUpdate);
@@ -3248,6 +6473,6 @@ function FpsIcon({ getFps, onClick }) {
3248
6473
  cancelAnimationFrame(timer);
3249
6474
  };
3250
6475
  }, [getFps]);
3251
- return (0, import_jsx_runtime26.jsx)(IconButton, { onClick, children: (0, import_jsx_runtime26.jsxs)("div", { className: "text", children: ["FPS", (0, import_jsx_runtime26.jsx)("br", {}), fps] }) });
6476
+ return (0, import_jsx_runtime27.jsx)(IconButton, { onClick, children: (0, import_jsx_runtime27.jsxs)("div", { className: "text", children: ["FPS", (0, import_jsx_runtime27.jsx)("br", {}), fps] }) });
3252
6477
  }
3253
6478
  //# sourceMappingURL=index.cjs.map